From c6c9deffc1035033ac2bda2542f20005fa6a31ab Mon Sep 17 00:00:00 2001 From: Jussi Surma-Aho <L4929@student.jamk.fi> Date: Thu, 20 Jun 2019 09:28:46 +0300 Subject: [PATCH] Can now select text in tooltip. Tooltip's marker is acting up, though --- src/App.css | 77 +++++---- src/components/DrawTools.js | 309 +++++++++++++++++++++--------------- src/components/UserMap.js | 110 ++++++------- 3 files changed, 282 insertions(+), 214 deletions(-) diff --git a/src/App.css b/src/App.css index c875df1..903311d 100644 --- a/src/App.css +++ b/src/App.css @@ -1,8 +1,3 @@ -/* - Do not touch classes with 'leaflet' in them! - They're Leaflet's own classes -*/ - body { margin: 0; padding: 0; @@ -65,22 +60,22 @@ div.fade-main { } div.sticky { - position: fixed; - top: 0px; - right: 0px; - height: 100px; - width: 100px; - margin-right: 150px; + position: fixed; + top: 0px; + right: 0px; + height: 100px; + width: 100px; + margin-right: 150px; } .close { - position: fixed; - color: #f1f1f1; - height: 85px; - font-size: 100px; - font-weight: bold; - transition: transform 0.4s ease-in-out; - line-height: 70%; + position: fixed; + color: #f1f1f1; + height: 85px; + font-size: 100px; + font-weight: bold; + transition: transform 0.4s ease-in-out; + line-height: 70%; } .close:hover, @@ -146,37 +141,55 @@ div.login button:hover { /* Editing text button in the toolbar */ .leaflet-draw-toolbar .leaflet-draw-draw-textbox { - background-image: url('icons/button-textbox.png'); + background-image: url("icons/button-textbox.png"); background-size: 30px 30px; } -/* by default leaflet doesn't allow selecting content in tooltips; overriding */ +/* Editing tooltips */ .leaflet-tooltip { - -webkit-user-select: text; - -moz-user-select: text; - -ms-user-select: text; - user-select: text; - pointer-events: auto; + font-size: 18px; + /* by default leaflet doesn't allow selecting content in tooltips; overriding */ + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + pointer-events: all; cursor: auto; + /* Overriding tooltip layout by making it invisible */ + background-color: transparent; + box-shadow: none; + border: none; + padding: 0; } -.leaflet-tooltip-top:before, -.leaflet-tooltip-bottom:before, -.leaflet-tooltip-left:before, -.leaflet-tooltip-right:before { - pointer-events: auto; +.leaflet-clickable { + pointer-events: all; } +/* moving the tooltip so that the icon can be clicked */ +/* +.leaflet-tooltip-center { + margin-top: 24px; +} +*/ + /* Editing editable tooltips */ .editable { cursor: text; /* the cursor icon doesn't change by default when hovering on top of the text; overriding */ - min-width: 120px; + min-width: 154px; min-height: 18px; + color: #fff; + font-weight: bold; + /* text borders */ + text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, + 1px 1px 0 #000; } /* placeholder text for tooltip */ -[contenteditable=true]:empty:before { +[contenteditable="true"]:empty:before { content: attr(placeholder); display: block; /* For Firefox */ color: #777; + text-shadow: none; + font-weight: normal; } diff --git a/src/components/DrawTools.js b/src/components/DrawTools.js index 666ff65..bc57aa4 100644 --- a/src/components/DrawTools.js +++ b/src/components/DrawTools.js @@ -1,143 +1,196 @@ -import React, {Component} from 'react'; -import {Â EditControl } from 'react-leaflet-draw' -import L from 'leaflet' -import 'leaflet-draw' -import { FeatureGroup } from 'react-leaflet' - -// empty icon for the text field -let emptyicon = L.icon({ - iconUrl: '../icons/nil.png', - iconSize: [1, 1], - iconAnchor: [1, 1] -}); +import React, { Component } from "react"; +import { EditControl } from "react-leaflet-draw"; +import L from "leaflet"; +import "leaflet-draw"; +import { FeatureGroup } from "react-leaflet"; // class for text field L.Draw.MarkerTextBox = L.Draw.Marker.extend({ - options: { - icon: emptyicon, - repeatMode: false, - interactive: true - }, - initialize: function (map, options) { - this.type = 'textbox'; // important to have a unique type, so that it won't get mixed up with other elements - this.featureTypeCode = 'textbox'; - L.Draw.Feature.prototype.initialize.call(this, map, options); - } + options: { + icon: L.divIcon({ + className: "dummy", + iconSize: [20, 20], + iconAnchor: [10, 50] + }), + repeatMode: false, + interactive: true + }, + initialize: function(map, options) { + this.type = "textbox"; // important to have a unique type, so that it won't get mixed up with other elements + this.featureTypeCode = "textbox"; + L.Draw.Feature.prototype.initialize.call(this, map, options); + } }); -L.DrawToolbar.include ({ - getModeHandlers: function(map) { - return [{ - enabled: this.options.polyline, - handler: new L.Draw.Polyline(map, this.options.polyline), - title: L.drawLocal.draw.toolbar.buttons.polyline - },{ - enabled: this.options.polygon, - handler: new L.Draw.Polygon(map, this.options.polygon), - title: L.drawLocal.draw.toolbar.buttons.polygon - },{ - enabled: this.options.rectangle, - handler: new L.Draw.Rectangle(map, this.options.rectangle), - title: L.drawLocal.draw.toolbar.buttons.rectangle - },{ - enabled: this.options.circle, - handler: new L.Draw.Circle(map, this.options.circle), - title: L.drawLocal.draw.toolbar.buttons.circle - },{ - enabled: this.options.marker, - handler: new L.Draw.Marker(map, this.options.marker), - title: L.drawLocal.draw.toolbar.buttons.marker - },{ - enabled: this.options.marker, - handler: new L.Draw.MarkerTextBox(map, this.options.marker), - title: 'Write text' - }]; - } +// Overriding default toolbar +// Just adding one new button, though, lol +L.DrawToolbar.include({ + getModeHandlers: function(map) { + return [ + { + enabled: this.options.polyline, + handler: new L.Draw.Polyline(map, this.options.polyline), + title: L.drawLocal.draw.toolbar.buttons.polyline + }, + { + enabled: this.options.polygon, + handler: new L.Draw.Polygon(map, this.options.polygon), + title: L.drawLocal.draw.toolbar.buttons.polygon + }, + { + enabled: this.options.rectangle, + handler: new L.Draw.Rectangle(map, this.options.rectangle), + title: L.drawLocal.draw.toolbar.buttons.rectangle + }, + { + enabled: this.options.circle, + handler: new L.Draw.Circle(map, this.options.circle), + title: L.drawLocal.draw.toolbar.buttons.circle + }, + { + enabled: this.options.marker, + handler: new L.Draw.Marker(map, this.options.marker), + title: L.drawLocal.draw.toolbar.buttons.marker + }, + { + enabled: this.options.marker, + handler: new L.Draw.MarkerTextBox(map, this.options.marker), + title: "Write text" + } + ]; + } }); class DrawTools extends Component { - constructor(props){ - super(props); - this.state = { - geoJSONAll: [] // property for all GeoJSON data in the map - } - } + constructor(props) { + super(props); + this.state = { + geoJSONAll: [] // property for all GeoJSON data in the map + }; + } + + _onCreated = e => { + // check if a drawn polyline has just one point in it + if (e.layerType === "polyline" && e.layer.getLatLngs().length === 1) { + e.layer.remove(); + return; + } + + if (e.layerType === "textbox") { + // have to create tooltip as a DOM element to allow text selecting. maybe + let tooltip = L.DomUtil.create("div", "editable"); + tooltip.innerHTML = + '<div contenteditable="true" placeholder="Click here and type"></div>'; + + e.layer.bindTooltip(tooltip, { + permanent: true, + direction: "center", + interactive: true + }); + + // disable dragging when mousedown is active on top of marker (tooltip) + // clicking on tooltip fires the marker's click handler, hence e.layer.on + e.layer.on("mousedown", function() { + L.DomEvent.disableClickPropagation(tooltip); + }); + + // show placeholder text again upon emptying textbox + e.layer.on("keyup", function() { + // when the text area is emptied, a <br> appears + // manually removing it so that the placeholder text can show + if ( + tooltip.innerHTML === + '<div placeholder="Click here and type" contenteditable="true"><br></div>' || + tooltip.innerHTML === + '<div placeholder="Click here and type" contenteditable="true"><div><br></div></div>' + ) { + tooltip.innerHTML = + '<div placeholder="Click here and type" contenteditable="true"></div>'; + } + + console.log(tooltip.firstChild.childNodes.length); + let margintop = tooltip.firstChild.childNodes.length * 40; + e.layer.options.icon.options.iconAnchor = [10, 1000]; + //tooltip.style.backgroundColor = "#000"; + console.log(e.layer); + //tooltip.style.marginTop = margintop.toString() + "px"; - _onCreated = (e) => { - // check if a drawn polyline has just one point in it - if (e.layerType === 'polyline' && e.layer.getLatLngs().length === 1) { - e.layer.remove(); - return; - } - // binding text field to textbox - // clicking on tooltip fires the marker's click handler - if (e.layerType === 'textbox') { - e.layer.bindTooltip('<div class="editable" contenteditable="true" placeholder="Click here and type"></div>', {permanent: true, direction: 'center', interactive: true}); - } - // turning layer data to GeoJSON - let layer = e.layer; + /* + let editable = document.querySelector(".editable"); + let editableStyle = getComputedStyle(editable); + let horizontalLoc = parseInt(editableStyle.height.replace("px", "")); + let verticalLoc = parseInt(editableStyle.width.replace("px", "")); + let trueHorizontalLoc = horizontalLoc + 40; + let trueVerticalLoc = verticalLoc + 10; + tooltip.style.marginTop = horizontalLoc.toString() + "px"; + */ + /* + editable.firstChild.style.marginTop = toString( + trueHorizontalLoc + "px" + ); + console.log("editable.firstChild.style: "); + console.log(editable.firstChild.style); + */ + }); + } + // turning layer data to GeoJSON this.makeGeoJSON(e.layer); - - /* Original GeoJSON code. Uncomment if needed - let geoJSON = layer.toGeoJSON(); - console.log(JSON.stringify(geoJSON, null, 4)); // makes the output readable in the console - */ - } + }; - makeGeoJSON = (e) => { + makeGeoJSON = e => { let geoJSON = e.toGeoJSON(); - let newAllGeoJSON = this.state.geoJSONAll; - newAllGeoJSON.push(geoJSON); - console.log(JSON.stringify(newAllGeoJSON, null, 4)); - this.setState({geoJSONAll: newAllGeoJSON}); - } + let newGeoJSONAll = this.state.geoJSONAll; + newGeoJSONAll.push(geoJSON); // can't do +=, need to use push function + console.log(JSON.stringify(newGeoJSONAll, null, 4)); + this.setState({ geoJSONAll: newGeoJSONAll }); + }; - render() { - return ( - // "It's important to wrap EditControl component into FeatureGroup component from react-leaflet. The elements you draw will be added to this FeatureGroup layer, when you hit edit button only items in this layer will be edited." - <FeatureGroup> - <EditControl - position='topright' - onCreated={this._onCreated} - draw={{ - circle: { - repeatMode: true, // allows using the tool again after finishing the previous shape - shapeOptions: { - color: '#f9f10c', - opacity: 1 // affects the outline only. for some reason it wasn't at full opacity, so this is needed for more clarity - } - }, - rectangle: { - repeatMode: true - }, - polygon: { - repeatMode: true, - allowIntersection: false, // Restricts shapes to simple polygons - drawError: { - color: '#e1e100', // Color the shape will turn when intersects - message: '<strong>Oh snap!<strong> you can\'t draw that!' // Message that will show when intersect - }, - shapeOptions: { - color: '#ed2572', - opacity: 1 - } - }, - polyline: { - repeatMode: true, - shapeOptions: { - color: '#ed2572', - opacity: 1 - } - }, - marker: { - repeatMode: false - }, - circlemarker: false - }} - /> - </FeatureGroup> - ) - } + render() { + return ( + // "It's important to wrap EditControl component into FeatureGroup component from react-leaflet. The elements you draw will be added to this FeatureGroup layer, when you hit edit button only items in this layer will be edited." + <FeatureGroup> + <EditControl + position="topright" + onCreated={this._onCreated} + draw={{ + circle: { + repeatMode: true, // allows using the tool again after finishing the previous shape + shapeOptions: { + color: "#f9f10c", + opacity: 1 // affects the outline only. for some reason it wasn't at full opacity, so this is needed for more clarity + } + }, + rectangle: { + repeatMode: true + }, + polygon: { + repeatMode: true, + allowIntersection: false, // Restricts shapes to simple polygons + drawError: { + color: "#e1e100", // Color the shape will turn when intersects + message: "<strong>Oh snap!<strong> you can't draw that!" // Message that will show when intersect + }, + shapeOptions: { + color: "#ed2572", + opacity: 1 + } + }, + polyline: { + repeatMode: true, + shapeOptions: { + color: "#ed2572", + opacity: 1 + } + }, + marker: { + repeatMode: false + }, + circlemarker: false + }} + /> + </FeatureGroup> + ); + } } -export default DrawTools; \ No newline at end of file +export default DrawTools; diff --git a/src/components/UserMap.js b/src/components/UserMap.js index b22033f..ae98a57 100644 --- a/src/components/UserMap.js +++ b/src/components/UserMap.js @@ -1,72 +1,70 @@ -import React, {Component} from 'react'; -import { - Map, - TileLayer, - ZoomControl, - Marker, - Popup -} from 'react-leaflet' -import L from 'leaflet' -import DrawTools from './DrawTools.js' +import React, { Component } from "react"; +import { Map, TileLayer, ZoomControl, Marker, Popup } from "react-leaflet"; +import L from "leaflet"; +import DrawTools from "./DrawTools.js"; class UserMap extends Component { - constructor(props){ + constructor(props) { super(props); this.state = { ownLat: null, ownLng: null, - mapUrl: 'https://tiles.kartat.kapsi.fi/taustakartta/{z}/{x}/{y}.jpg', - bounds: L.latLngBounds(18.786621, 59.337183) - } + mapUrl: "https://tiles.kartat.kapsi.fi/taustakartta/{z}/{x}/{y}.jpg" + }; this.watchPositionId = null; } - componentDidMount(){ - this.getCurrentPosition((position) => { + componentDidMount() { + this.getCurrentPosition(position => { this.setCurrentPosition(position); }); } - componentWillUnmount(){ - if(this.watchPositionId != null){ + componentWillUnmount() { + if (this.watchPositionId != null) { navigator.geolocation.clearWatch(this.watchPositionId); } } - setCurrentPosition(position){ + setCurrentPosition(position) { this.setState({ ownLat: position.coords.latitude, - ownLng: position.coords.longitude, + ownLng: position.coords.longitude }); } - getCurrentPosition(callback){ - if(!navigator.geolocation){ + getCurrentPosition(callback) { + if (!navigator.geolocation) { console.log("Can't get geolocation :/"); - } - else{ + } else { // Position tracking options const options = { enableHighAccuracy: true, timeout: 30000, maximumAge: 0 + }; + + if (this.watchPositionId != null) { + navigator.geolocation.clearWatch(this.watchPositionId); } - if(this.watchPositionId != null){navigator.geolocation.clearWatch(this.watchPositionId);} - - this.watchPositionId = navigator.geolocation.watchPosition((position) =>{ - //success - if(position != null){ - callback(position); - } - }, (error) =>{ - console.log(error); - }, options); + this.watchPositionId = navigator.geolocation.watchPosition( + position => { + //success + if (position != null) { + callback(position); + } + }, + error => { + console.log(error); + }, + options + ); } } - positionToGeoJSON(position){ + positionToGeoJSON(position) { let geoJSON = { type: "Feature", properties: {}, @@ -74,47 +72,51 @@ class UserMap extends Component { type: "Point", coordinates: [position.coords.longitude, position.coords.latitude] } - } + }; return JSON.stringify(geoJSON); } - testers = (asd) => { + testers = asd => { console.log(asd.target.getZoom()); - } + }; render() { return ( - <Map - className='map' + <Map + className="map" center={this.props.position} zoom={this.props.zoom} - minZoom='7' - maxZoom='17' + minZoom="7" + maxZoom="17" // onzoomend={this.testers} // getting the zoom level - maxBounds={this.props.bounds} // maxBounds settings don't work for now, for some reason - maxBoundsViscosity='1' - zoomControl={false} /* remove the default zoom control button at the top left corner */ > + zoomControl={ + false + } /* remove the default zoom control button at the top left corner */ + > <TileLayer attribution='© <a href="https://www.maanmittauslaitos.fi/">Maanmittauslaitos</a>' url={this.props.mapUrl} /> - - <ZoomControl position='topright' /> - <DrawTools position={this.props.position} /> + + <ZoomControl position="topright" /> + <DrawTools position={this.props.position} /> <Marker position={this.props.position}> <Popup> Se on perjantai, my dudes <br /> </Popup> </Marker> - {this.state.ownLat !== null && <Marker position={[this.state.ownLat, this.state.ownLng]}> - <Popup> - User's real position.<br /> - </Popup> - </Marker>} + {this.state.ownLat !== null && ( + <Marker position={[this.state.ownLat, this.state.ownLng]}> + <Popup> + User's real position. + <br /> + </Popup> + </Marker> + )} </Map> ); } } -export default UserMap; \ No newline at end of file +export default UserMap; -- GitLab