@stubber/form-fields 2.1.0 → 2.1.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.
|
@@ -5,10 +5,24 @@ export const map_field_param_spec = {
|
|
|
5
5
|
spec: {
|
|
6
6
|
fields: {
|
|
7
7
|
hide_geolocate_button: {
|
|
8
|
-
fieldtype: "checkbox"
|
|
8
|
+
fieldtype: "checkbox",
|
|
9
|
+
without_value_details: true
|
|
9
10
|
},
|
|
10
11
|
auto_set_location: {
|
|
11
|
-
fieldtype: "checkbox"
|
|
12
|
+
fieldtype: "checkbox",
|
|
13
|
+
without_value_details: true
|
|
14
|
+
},
|
|
15
|
+
address_mode: {
|
|
16
|
+
fieldtype: "select",
|
|
17
|
+
without_value_details: true,
|
|
18
|
+
params: {
|
|
19
|
+
options: [
|
|
20
|
+
{ label: "None", value: "none" },
|
|
21
|
+
{ label: "Hidden", value: "hidden" },
|
|
22
|
+
{ label: "Readonly", value: "readonly" },
|
|
23
|
+
{ label: "Editable", value: "editable" }
|
|
24
|
+
]
|
|
25
|
+
}
|
|
12
26
|
}
|
|
13
27
|
}
|
|
14
28
|
}
|
|
@@ -17,10 +31,14 @@ export const map_field_param_spec = {
|
|
|
17
31
|
|
|
18
32
|
<script>import { controls, Geocoder, Map, Marker } from "@beyonk/svelte-mapbox";
|
|
19
33
|
import FieldLabel from "../FieldLabel.svelte";
|
|
34
|
+
import { mapValues, omit } from "lodash-es";
|
|
35
|
+
import { Input } from "@stubber/ui/input";
|
|
36
|
+
import { Label } from "@stubber/ui/label";
|
|
20
37
|
const { GeolocateControl } = controls;
|
|
21
38
|
export let fieldStore;
|
|
22
39
|
const initial_value = $fieldStore.value;
|
|
23
40
|
let params = $fieldStore.params;
|
|
41
|
+
let reverse_geocode_enabled = params?.address_mode && params?.address_mode !== "none";
|
|
24
42
|
let hide_geolocate_button = params?.hide_geolocate_button || false;
|
|
25
43
|
let auto_set_location = params?.auto_set_location || false;
|
|
26
44
|
let dependencies = $fieldStore?.formDependencies;
|
|
@@ -58,6 +76,39 @@ function setValueCoords(lat, lng, force = false) {
|
|
|
58
76
|
lng: lng ?? null,
|
|
59
77
|
latlng: `${lat ?? null},${lng ?? null}`
|
|
60
78
|
};
|
|
79
|
+
reverse_geocode(lat, lng);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function reverse_geocode(lat, lng) {
|
|
83
|
+
if (!mapboxAccessToken || !reverse_geocode_enabled) return;
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch(
|
|
86
|
+
`https://api.mapbox.com/search/geocode/v6/reverse?longitude=${lng}&latitude=${lat}&access_token=${mapboxAccessToken}`
|
|
87
|
+
);
|
|
88
|
+
const data = await response.json();
|
|
89
|
+
if (data?.features?.length > 0) {
|
|
90
|
+
const feature = data?.features[0];
|
|
91
|
+
if (feature?.properties?.feature_type === "address") {
|
|
92
|
+
const full_address = feature?.properties?.full_address;
|
|
93
|
+
const cleaned_context = mapValues(
|
|
94
|
+
feature?.properties?.context,
|
|
95
|
+
(v) => omit(v, ["mapbox_id", "wikidata_id", "alternate"])
|
|
96
|
+
);
|
|
97
|
+
const address = {};
|
|
98
|
+
if (full_address) {
|
|
99
|
+
address.full_address = full_address;
|
|
100
|
+
}
|
|
101
|
+
if (cleaned_context) {
|
|
102
|
+
address["details"] = cleaned_context;
|
|
103
|
+
}
|
|
104
|
+
$fieldStore.value = {
|
|
105
|
+
...$fieldStore.value,
|
|
106
|
+
address
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error("reverse geocode error", error);
|
|
61
112
|
}
|
|
62
113
|
}
|
|
63
114
|
function resetValues() {
|
|
@@ -79,22 +130,34 @@ function handle_geolocate(o) {
|
|
|
79
130
|
const coords = o?.detail?.coords || {};
|
|
80
131
|
setValueCoords(coords.latitude, coords.longitude);
|
|
81
132
|
}
|
|
133
|
+
function handle_address_change(e) {
|
|
134
|
+
const new_address = e?.target?.value || "";
|
|
135
|
+
if (new_address && $fieldStore.value.address.full_address !== new_address) {
|
|
136
|
+
$fieldStore.value = {
|
|
137
|
+
...$fieldStore.value,
|
|
138
|
+
address: {
|
|
139
|
+
...$fieldStore.value.address,
|
|
140
|
+
full_address: new_address
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
82
145
|
</script>
|
|
83
146
|
|
|
84
147
|
<FieldLabel {fieldStore} />
|
|
85
148
|
{#if mapboxAccessToken}
|
|
86
|
-
<div class="flex flex-col
|
|
149
|
+
<div class="flex w-full flex-col">
|
|
87
150
|
<div>
|
|
88
151
|
<button
|
|
89
152
|
type="button"
|
|
90
153
|
on:click={resetValues}
|
|
91
|
-
class="text-sm text-surface-300 hover:
|
|
154
|
+
class="text-sm text-surface-300 hover:text-surface-500 hover:underline"
|
|
92
155
|
>
|
|
93
156
|
<i class="fa-regular fa-undo" /> Reset
|
|
94
157
|
</button>
|
|
95
158
|
</div>
|
|
96
159
|
<div class="relative mt-2 rounded-md">
|
|
97
|
-
<div class="w-full
|
|
160
|
+
<div class="h-72 w-full">
|
|
98
161
|
<Map
|
|
99
162
|
bind:center
|
|
100
163
|
zoom={14}
|
|
@@ -114,31 +177,60 @@ function handle_geolocate(o) {
|
|
|
114
177
|
<GeolocateControl position="top-right" on:geolocate={handle_geolocate} />
|
|
115
178
|
{/if}
|
|
116
179
|
{#if browserLocation}
|
|
117
|
-
<Marker
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
180
|
+
<Marker
|
|
181
|
+
lat={browserLocation.lat}
|
|
182
|
+
lng={browserLocation.lng}
|
|
183
|
+
markerOptions={{ anchor: "center" }}
|
|
184
|
+
>
|
|
185
|
+
<div class="relative flex flex-col items-center">
|
|
186
|
+
<span class="fa-stack fa-2x">
|
|
187
|
+
<i
|
|
188
|
+
class="fa-solid fa-circle fa-stack-2x text-[1.3em] leading-[inherit] text-white"
|
|
189
|
+
/>
|
|
190
|
+
<i class="fa-solid fa-user fa-stack-1x text-blue-600" />
|
|
191
|
+
</span>
|
|
124
192
|
</div>
|
|
125
193
|
</Marker>
|
|
126
194
|
{/if}
|
|
127
195
|
{#if $fieldStore.value?.lat && $fieldStore.value?.lng}
|
|
128
|
-
<Marker
|
|
129
|
-
|
|
196
|
+
<Marker
|
|
197
|
+
lat={$fieldStore.value.lat}
|
|
198
|
+
lng={$fieldStore.value.lng}
|
|
199
|
+
popup={false}
|
|
200
|
+
markerOptions={{ anchor: "bottom" }}
|
|
201
|
+
>
|
|
202
|
+
<div class="relative flex flex-col items-center">
|
|
130
203
|
<div
|
|
131
|
-
class="bg-
|
|
204
|
+
class="rounded-xl bg-white px-2 py-1 text-sm font-medium text-slate-800 shadow-lg ring-1 ring-black/20"
|
|
132
205
|
>
|
|
133
|
-
lat: {$fieldStore.value.lat?.toString()?.substring(0,
|
|
134
|
-
lng: {$fieldStore.value.lng?.toString()?.substring(0,
|
|
206
|
+
<div>lat: {$fieldStore.value.lat?.toString()?.substring(0, 7)}</div>
|
|
207
|
+
<div>lng: {$fieldStore.value.lng?.toString()?.substring(0, 7)}</div>
|
|
135
208
|
</div>
|
|
209
|
+
|
|
210
|
+
<span class="fa-stack fa-2x h-[1.5em]">
|
|
211
|
+
<i
|
|
212
|
+
class="fa-solid fa-location-pin fa-stack-2x text-[1.2em] leading-[inherit] text-white"
|
|
213
|
+
/>
|
|
214
|
+
<i class="fa-solid fa-location-dot fa-stack-1x text-green-600" />
|
|
215
|
+
</span>
|
|
136
216
|
</div>
|
|
137
217
|
</Marker>
|
|
138
218
|
{/if}
|
|
139
219
|
</Map>
|
|
140
220
|
</div>
|
|
141
221
|
</div>
|
|
222
|
+
{#if params?.address_mode === "editable"}
|
|
223
|
+
<Input
|
|
224
|
+
type="text"
|
|
225
|
+
class="mt-1"
|
|
226
|
+
value={$fieldStore.value?.address?.full_address || ".."}
|
|
227
|
+
on:change={handle_address_change}
|
|
228
|
+
/>
|
|
229
|
+
{:else if params?.address_mode === "readonly"}
|
|
230
|
+
<Label class="mt-1 block text-sm font-medium text-surface-700"
|
|
231
|
+
>{$fieldStore.value?.address?.full_address || ".."}</Label
|
|
232
|
+
>
|
|
233
|
+
{/if}
|
|
142
234
|
</div>
|
|
143
235
|
{/if}
|
|
144
236
|
<FieldMessage {fieldStore} />
|
|
@@ -4,6 +4,7 @@ import { type IBaseField, type IBuiltField, type IInitialForm } from "../interfa
|
|
|
4
4
|
export interface IMapFieldParams {
|
|
5
5
|
hide_geolocate_button?: boolean;
|
|
6
6
|
auto_set_location?: boolean;
|
|
7
|
+
address_mode?: "none" | "hidden" | "readonly" | "editable";
|
|
7
8
|
}
|
|
8
9
|
export interface IMapField extends IBaseField<IMapFieldParams> {
|
|
9
10
|
fieldtype: "map";
|