diff --git a/frappe/public/js/frappe/form/controls/geolocation.js b/frappe/public/js/frappe/form/controls/geolocation.js
index aa75a2282b..ada729fd66 100644
--- a/frappe/public/js/frappe/form/controls/geolocation.js
+++ b/frappe/public/js/frappe/form/controls/geolocation.js
@@ -5,26 +5,28 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f
async make() {
super.make();
+ $(this.input_area).addClass("hidden");
}
- make_wrapper() {
+ set_disp_area(value) {
// Create the elements for map area
- super.make_wrapper();
+ if (!this.disp_area) {
+ return;
+ }
- let $input_wrapper = this.$wrapper.find(".control-input-wrapper");
this.map_id = frappe.dom.get_unique_id();
this.map_area = $(
`
`
);
- this.map_area.prependTo($input_wrapper);
- this.$wrapper.find(".control-input").addClass("hidden");
+
+ $(this.disp_area).html(this.map_area);
+ $(this.disp_area).removeClass("like-disabled-input");
+ $(this.disp_area).css("display", "block");
if (this.frm) {
- this.make_map();
+ this.make_map(value);
} else {
$(document).on("frappe.ui.Dialog:shown", () => {
this.make_map();
@@ -32,7 +34,7 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f
}
}
- make_map() {
+ make_map(value) {
this.bind_leaflet_map();
if (this.disabled) {
this.map.dragging.disable();
@@ -44,52 +46,50 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f
this.map.zoomControl.remove();
} else {
this.bind_leaflet_draw_control();
+ this.bind_leaflet_event_listeners();
this.bind_leaflet_locate_control();
- this.bind_leaflet_refresh_button();
+ this.bind_leaflet_data(value);
}
- this.map.setView(frappe.utils.map_defaults.center, frappe.utils.map_defaults.zoom);
}
- format_for_input(value) {
- if (!this.map) return;
- // render raw value from db into map
- this.clear_editable_layers();
- if (value) {
- var data_layers = new L.FeatureGroup().addLayer(
- L.geoJson(JSON.parse(value), {
- pointToLayer: function (geoJsonPoint, latlng) {
- if (geoJsonPoint.properties.point_type == "circle") {
- return L.circle(latlng, { radius: geoJsonPoint.properties.radius });
- } else if (geoJsonPoint.properties.point_type == "circlemarker") {
- return L.circleMarker(latlng, {
- radius: geoJsonPoint.properties.radius,
- });
- } else {
- return L.marker(latlng);
- }
- },
- })
- );
- this.add_non_group_layers(data_layers, this.editableLayers);
- try {
- this.map.fitBounds(this.editableLayers.getBounds(), {
- padding: [50, 50],
- });
- } catch (err) {
- // suppress error if layer has a point.
- }
- this.editableLayers.addTo(this.map);
- } else {
- this.map.setView(frappe.utils.map_defaults.center, frappe.utils.map_defaults.zoom);
+ bind_leaflet_data(value) {
+ /* render raw value from db into map */
+ if (!this.map || !value) {
+ return;
+ }
+ this.clear_editable_layers();
+
+ const data_layers = new L.FeatureGroup().addLayer(
+ L.geoJson(JSON.parse(value), { pointToLayer: this.point_to_layer })
+ );
+ this.add_non_group_layers(data_layers, this.editableLayers);
+ this.editableLayers.addTo(this.map);
+ this.fit_and_recenter_map();
+ }
+
+ /**
+ * Defines custom rules for how geoJSON data is rendered on the map.
+ *
+ * @param {Object} geoJsonPoint - The geoJSON object to be rendered on the map.
+ * @param {Object} latlng - The latitude and longitude where the geoJSON data should be rendered on the map.
+ * @returns {Object} - Returns the Leaflet layer object to be rendered on the map.
+ */
+ point_to_layer(geoJsonPoint, latlng) {
+ // Custom rules for how geojson data is rendered on the map
+ if (geoJsonPoint.properties.point_type == "circle") {
+ return L.circle(latlng, { radius: geoJsonPoint.properties.radius });
+ } else if (geoJsonPoint.properties.point_type == "circlemarker") {
+ return L.circleMarker(latlng, { radius: geoJsonPoint.properties.radius });
+ } else {
+ return L.marker(latlng);
}
- this.map.invalidateSize();
}
bind_leaflet_map() {
- var circleToGeoJSON = L.Circle.prototype.toGeoJSON;
+ const circleToGeoJSON = L.Circle.prototype.toGeoJSON;
L.Circle.include({
toGeoJSON: function () {
- var feature = circleToGeoJSON.call(this);
+ const feature = circleToGeoJSON.call(this);
feature.properties = {
point_type: "circle",
radius: this.getRadius(),
@@ -100,7 +100,7 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f
L.CircleMarker.include({
toGeoJSON: function () {
- var feature = circleToGeoJSON.call(this);
+ const feature = circleToGeoJSON.call(this);
feature.properties = {
point_type: "circlemarker",
radius: this.getRadius(),
@@ -111,10 +111,13 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f
L.Icon.Default.imagePath = "/assets/frappe/images/leaflet/";
this.map = L.map(this.map_id);
+ this.map.setView(frappe.utils.map_defaults.center, frappe.utils.map_defaults.zoom);
L.tileLayer(frappe.utils.map_defaults.tiles, frappe.utils.map_defaults.options).addTo(
this.map
);
+
+ this.editableLayers = new L.FeatureGroup();
}
bind_leaflet_locate_control() {
@@ -124,9 +127,18 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f
}
bind_leaflet_draw_control() {
- this.editableLayers = new L.FeatureGroup();
+ if (
+ !frappe.perm.has_perm(this.doctype, this.df.permlevel, "write", this.doc) ||
+ this.df.read_only
+ ) {
+ return;
+ }
- var options = {
+ this.map.addControl(this.get_leaflet_controls());
+ }
+
+ get_leaflet_controls() {
+ return new L.Control.Draw({
position: "topleft",
draw: {
polyline: {
@@ -156,12 +168,10 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f
featureGroup: this.editableLayers, //REQUIRED!!
remove: true,
},
- };
-
- // create control and add to map
- this.drawControl = new L.Control.Draw(options);
- this.map.addControl(this.drawControl);
+ });
+ }
+ bind_leaflet_event_listeners() {
this.map.on("draw:created", (e) => {
var type = e.layerType,
layer = e.layer;
@@ -173,31 +183,12 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f
});
this.map.on("draw:deleted draw:edited", (e) => {
- var layer = e.layer;
+ const { layer } = e;
this.editableLayers.removeLayer(layer);
this.set_value(JSON.stringify(this.editableLayers.toGeoJSON()));
});
}
- bind_leaflet_refresh_button() {
- L.easyButton({
- id: "refresh-map-" + this.df.fieldname,
- position: "topright",
- type: "replace",
- leafletClasses: true,
- states: [
- {
- stateName: "refresh-map",
- onClick: function (button, map) {
- map._onResize();
- },
- title: "Refresh map",
- icon: "fa fa-refresh",
- },
- ],
- }).addTo(this.map);
- }
-
add_non_group_layers(source_layer, target_group) {
// https://gis.stackexchange.com/a/203773
// Would benefit from https://github.com/Leaflet/Leaflet/issues/4461
@@ -215,4 +206,20 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f
this.editableLayers.removeLayer(l);
});
}
+
+ fit_and_recenter_map() {
+ // Spread map across the wrapper, recenter and zoom w.r.t bounds
+ try {
+ this.map.invalidateSize();
+ this.map.fitBounds(this.editableLayers.getBounds(), {
+ padding: [50, 50],
+ });
+ } catch (err) {
+ // suppress error if layer has a point.
+ }
+ }
+
+ on_section_collapse(hide) {
+ !hide && this.fit_and_recenter_map();
+ }
};
diff --git a/frappe/public/js/frappe/form/controls/signature.js b/frappe/public/js/frappe/form/controls/signature.js
index 0cbc1f3c26..6ab96012f1 100644
--- a/frappe/public/js/frappe/form/controls/signature.js
+++ b/frappe/public/js/frappe/form/controls/signature.js
@@ -133,4 +133,7 @@ frappe.ui.form.ControlSignature = class ControlSignature extends frappe.ui.form.
this.set_my_value(base64_img);
this.set_image(this.get_value());
}
+ on_section_collapse() {
+ this.refresh();
+ }
};
diff --git a/frappe/public/js/frappe/form/section.js b/frappe/public/js/frappe/form/section.js
index a692cbac0d..b4908e749a 100644
--- a/frappe/public/js/frappe/form/section.js
+++ b/frappe/public/js/frappe/form/section.js
@@ -110,12 +110,7 @@ export default class Section {
this.set_icon(hide);
- // refresh signature fields
- this.fields_list.forEach((f) => {
- if (f.df.fieldtype == "Signature") {
- f.refresh();
- }
- });
+ this.fields_list.forEach((f) => f.on_section_collapse && f.on_section_collapse(hide));
// save state for next reload ('' is falsy)
if (this.df.css_class)