diff --git a/altrpnjs/app/altrp-templates/styles/elements/map.css b/altrpnjs/app/altrp-templates/styles/elements/map.css index 3c405ac1a..adba85e59 100644 --- a/altrpnjs/app/altrp-templates/styles/elements/map.css +++ b/altrpnjs/app/altrp-templates/styles/elements/map.css @@ -108,4 +108,8 @@ .altrp-map__container { background-color: #dddddd; +} + +.altrp-location__container { + background-color: #dddddd; } \ No newline at end of file diff --git a/altrpnjs/helpers/const/DEFAULT_REACT_ELEMENTS.ts b/altrpnjs/helpers/const/DEFAULT_REACT_ELEMENTS.ts index 2e22ed725..3d4f8393a 100644 --- a/altrpnjs/helpers/const/DEFAULT_REACT_ELEMENTS.ts +++ b/altrpnjs/helpers/const/DEFAULT_REACT_ELEMENTS.ts @@ -32,6 +32,7 @@ const DEFAULT_REACT_ELEMENTS = [ 'carousel', 'map', 'map_builder', + 'location', 'menu', 'pie-diagram', 'line-diagram', diff --git a/altrpnjs/helpers/widgets-renders/renderLocation.ts b/altrpnjs/helpers/widgets-renders/renderLocation.ts new file mode 100644 index 000000000..bdfda276b --- /dev/null +++ b/altrpnjs/helpers/widgets-renders/renderLocation.ts @@ -0,0 +1,47 @@ +import objectToStylesString from "../objectToStylesString" +import getResponsiveSetting from "../getResponsiveSetting" +//@ts-ignore +export default function renderLocation(settings, device, context) { + const height = getResponsiveSetting(settings, 'style_height', device, {size: 400, unit: 'px'}) + const margin = getResponsiveSetting(settings, 'style_margin', device, {top: 0, bottom: 0, left: 0, right: 0, unit: 'px'}) + + const styles = { + height: height.size + height.unit, + marginTop: margin?.top + margin?.unit, + marginBottom: margin?.bottom + margin?.unit, + marginLeft: margin?.left + margin?.unit, + marginRight: margin?.right + margin?.unit, + pointerEvents: 'auto', + } + + return ` +
+
+
+
+ +
+
+
+
+
+
+ + Leaflet + + | © + + OpenStreetMap + + contributors +
+
+
+
+
+ ` +} diff --git a/altrpnjs/start/view.ts b/altrpnjs/start/view.ts index 247036f00..52f87f227 100644 --- a/altrpnjs/start/view.ts +++ b/altrpnjs/start/view.ts @@ -11,6 +11,7 @@ import renderInputAccept from '../helpers/widgets-renders/renderInputAccept'; import renderInputHidden from '../helpers/widgets-renders/renderInputHidden'; import renderInputFile from '../helpers/widgets-renders/renderInputFile'; import renderMap from '../helpers/widgets-renders/renderMap'; +import renderLocation from '../helpers/widgets-renders/renderLocation'; import renderHeadingTypeAnimating from '../helpers/widgets-renders/renderHeadingTypeAnimating'; import renderImageLightbox from '../helpers/widgets-renders/renderImageLightbox'; import renderTree from '../helpers/widgets-renders/renderTree'; @@ -119,6 +120,7 @@ View.global('renderInputHidden', renderInputHidden) View.global('renderInputFile', renderInputFile) View.global('renderInputPagination', renderInputPagination) View.global('renderMap', renderMap) +View.global('renderLocation', renderLocation) View.global('renderHeadingTypeAnimating', renderHeadingTypeAnimating) View.global('renderImageLightbox', renderImageLightbox) View.global('renderTree', renderTree) diff --git a/app/Helpers/functions.php b/app/Helpers/functions.php index 3a06e5f89..b00424a8a 100644 --- a/app/Helpers/functions.php +++ b/app/Helpers/functions.php @@ -1050,6 +1050,7 @@ function _extractElementsNames( $element, &$elementNames, $only_react_elements 'map', 'text', 'map_builder', + 'location', 'menu', 'pie-diagram', 'line-diagram', diff --git a/resources/modules/editor/src/js/classes/elements/Location.js b/resources/modules/editor/src/js/classes/elements/Location.js new file mode 100644 index 000000000..50f09a764 --- /dev/null +++ b/resources/modules/editor/src/js/classes/elements/Location.js @@ -0,0 +1,107 @@ +import BaseElement from "./BaseElement"; +import LocationIcon from "../../../svgs/location.svg"; +import { advancedTabControllers } from "../../decorators/register-controllers"; +import { + CONTROLLER_SWITCHER, + CONTROLLER_NUMBER, + CONTROLLER_DIMENSIONS, + CONTROLLER_SLIDER, + CONTROLLER_EVENT_HANDLER, + TAB_CONTENT, + TAB_STYLE, +} from "../modules/ControllersManager"; + +class Location extends BaseElement { + static getName() { + return "location"; + } + + static getTitle() { + return "Location"; + } + + static getIconComponent() { + return LocationIcon; + } + + static getType() { + return "widget"; + } + + static getGroup() { + return "Advanced"; + } + + _registerControls() { + if (this.controllersRegistered) { + return; + } + + this.startControlSection("content_section", { + tab: TAB_CONTENT, + label: "Content", + }); + + this.addControl("canvas", { + type: CONTROLLER_SWITCHER, + label: "Canvas", + default: true, + locked: true, + }); + + this.addControl("zoom", { + type: CONTROLLER_NUMBER, + label: "Zoom", + default: 6, + locked: true, + }); + + this.addControl("handler", { + type: CONTROLLER_EVENT_HANDLER, + label: "Event handler", + default: { + evt: "", + params: "", + }, + locked: true, + }); + + this.endControlSection(); + + this.startControlSection("style", { + tab: TAB_STYLE, + label: "Size", + }); + + this.addControl("style_height", { + type: CONTROLLER_SLIDER, + label: "height", + default: { + size: 400, + unit: "px", + }, + units: ["px", "%", "vh"], + max: 1000, + min: 0, + locked: true, + }); + + this.addControl("style_margin", { + type: CONTROLLER_DIMENSIONS, + label: "Margin", + default: { + top: 10, + right: 10, + bottom: 10, + left: 10, + unit: "px", + bind: true, + }, + units: ["px", "%", "vh"], + locked: true, + }); + + advancedTabControllers(this); + } +} +export default Location; diff --git a/resources/modules/editor/src/js/components/ElementWrapper.js b/resources/modules/editor/src/js/components/ElementWrapper.js index cadd75e67..4bb24a2f0 100644 --- a/resources/modules/editor/src/js/components/ElementWrapper.js +++ b/resources/modules/editor/src/js/components/ElementWrapper.js @@ -36,6 +36,7 @@ import { getPostsStyles } from "../../../../front-app/src/js/components/helpers/ import FormComponent from "./widgets/styled-components/FormComponent"; import MapComponent from "./widgets/styled-components/MapComponent"; import MapConstructorComponent from "./widgets/styled-components/MapConstructorComponent"; +import LocationComponent from "./widgets/styled-components/LocationComponent"; import AdvancedComponent from "./widgets/styled-components/AdvancedComponent"; import { getEditor, @@ -326,6 +327,9 @@ const ElementWrapperGlobalStyles = window.createGlobalStyle`${({ case "map_builder": styles += `.${prefix}${elementId} {${MapConstructorComponent(settings)}}`; break; + case "location": + styles += `.${prefix}${elementId} {${LocationComponent(settings)}}`; + break; case "scheduler": styles += `.${prefix}${elementId} {${getSchedulerStyles(settings, elementId)}}`; break; diff --git a/resources/modules/editor/src/js/components/altrp-location/AltrpLocation.js b/resources/modules/editor/src/js/components/altrp-location/AltrpLocation.js new file mode 100644 index 000000000..4f1169b53 --- /dev/null +++ b/resources/modules/editor/src/js/components/altrp-location/AltrpLocation.js @@ -0,0 +1,47 @@ +import React, { useEffect, useState } from "react"; +import LocationDesigner from "./LocationDesigner"; + +function AltrpLocation({ element, settings, classes }) { + const [isLoading, setIsLoading] = useState(true); + const [currentPosition, setCurrentPosition] = useState({ latitude: 0, longitude: 0 }); + const { + canvas, + style_height = {}, + style_margin = {} + } = settings; + + useEffect(() => { + if (window) { + window.navigator.geolocation.getCurrentPosition(pos => { + setCurrentPosition({ + latitude: pos.coords.latitude, + longitude: pos.coords.longitude + }); + setIsLoading(false); + }) + } + }, [window]); + + return ( + + ); +} + +export default AltrpLocation; diff --git a/resources/modules/editor/src/js/components/altrp-location/DivIcon.js b/resources/modules/editor/src/js/components/altrp-location/DivIcon.js new file mode 100644 index 000000000..44a47cb40 --- /dev/null +++ b/resources/modules/editor/src/js/components/altrp-location/DivIcon.js @@ -0,0 +1,26 @@ +import React from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import { divIcon } from "leaflet/src/layer/marker/DivIcon"; +import MemoHomeIcon from "./Icons/HomeIcon"; +import MemoMarkerIcon from "./Icons/MarkerIcon"; +import MemoGoogleMarkerIcon from "./Icons/GoogleMarkerIcon"; + +export const iconTypes = { + Marker: MemoMarkerIcon, + GoogleMarker: MemoGoogleMarkerIcon, + Home: MemoHomeIcon, +}; + +export const customIcon = ( + name = "GoogleMarker", + color = "#3388ff", + size = [36, 36] +) => { + const Icon = iconTypes[name] ?? iconTypes.GoogleMarker; + + const html = renderToStaticMarkup( + + ); + + return new divIcon({ html }); +}; diff --git a/resources/modules/editor/src/js/components/altrp-location/Icons/GoogleMarkerIcon.js b/resources/modules/editor/src/js/components/altrp-location/Icons/GoogleMarkerIcon.js new file mode 100644 index 000000000..daa7ed251 --- /dev/null +++ b/resources/modules/editor/src/js/components/altrp-location/Icons/GoogleMarkerIcon.js @@ -0,0 +1,14 @@ +import React from "react"; + +function GoogleMarkerIcon(props) { + return ( + + + + + + ); +} + +const MemoGoogleMarkerIcon = React.memo(GoogleMarkerIcon); +export default MemoGoogleMarkerIcon; diff --git a/resources/modules/editor/src/js/components/altrp-location/Icons/HomeIcon.js b/resources/modules/editor/src/js/components/altrp-location/Icons/HomeIcon.js new file mode 100644 index 000000000..0a699f6dd --- /dev/null +++ b/resources/modules/editor/src/js/components/altrp-location/Icons/HomeIcon.js @@ -0,0 +1,14 @@ +import React from "react"; + +function HomeIcon(props) { + return ( + + + + + + ); +} + +const MemoHomeIcon = React.memo(HomeIcon); +export default MemoHomeIcon; diff --git a/resources/modules/editor/src/js/components/altrp-location/Icons/MarkerIcon.js b/resources/modules/editor/src/js/components/altrp-location/Icons/MarkerIcon.js new file mode 100644 index 000000000..cda5f555b --- /dev/null +++ b/resources/modules/editor/src/js/components/altrp-location/Icons/MarkerIcon.js @@ -0,0 +1,35 @@ +import React from "react"; + +function MarkerIcon(props) { + return ( + + + + + + + + + ); +} + +const MemoMarkerIcon = React.memo(MarkerIcon); +export default MemoMarkerIcon; diff --git a/resources/modules/editor/src/js/components/altrp-location/Loader.js b/resources/modules/editor/src/js/components/altrp-location/Loader.js new file mode 100644 index 000000000..ead0909ac --- /dev/null +++ b/resources/modules/editor/src/js/components/altrp-location/Loader.js @@ -0,0 +1,14 @@ +import React from "react"; +import Spinner from "react-bootstrap/Spinner"; + +function Loader() { + return ( +
+ + Loading... + +
+ ); +} + +export default Loader; diff --git a/resources/modules/editor/src/js/components/altrp-location/LocationDesigner.js b/resources/modules/editor/src/js/components/altrp-location/LocationDesigner.js new file mode 100644 index 000000000..95db72835 --- /dev/null +++ b/resources/modules/editor/src/js/components/altrp-location/LocationDesigner.js @@ -0,0 +1,54 @@ +import React from "react"; +import "leaflet/dist/leaflet.css"; +import { + MapContainer, + Marker, + Tooltip, + useMap, +} from "react-leaflet"; +import clsx from "clsx"; +import Loader from "./Loader"; +import TileLayer from "./TileLayer"; +import { customIcon } from "./DivIcon"; + +function ChangeView({ center, zoom }) { + const map = useMap(); + map.setView(center, zoom); + return null; +} + +function LocationDesigner({ + className, + center, + zoom, + isLoading = false, + interactionOptions = {}, + style = {}, + classes +}) { + return ( +
+ {isLoading && } + + + + + + + + Current Location + + +
+ ); +} + +export default LocationDesigner; diff --git a/resources/modules/editor/src/js/components/altrp-location/TileLayer.js b/resources/modules/editor/src/js/components/altrp-location/TileLayer.js new file mode 100644 index 000000000..25c415a07 --- /dev/null +++ b/resources/modules/editor/src/js/components/altrp-location/TileLayer.js @@ -0,0 +1,95 @@ +import { TileLayer as NativeTileLayer } from "react-leaflet"; + +export const MapTypes = [ + { + key: "street", + label: "Street", + }, + { + key: "terrain", + label: "Terrain", + }, + { + key: "satellite", + label: "Satellite", + }, + { + key: "dark", + label: "Dark", + }, + { + key: "monolight", + label: "Mono Light", + }, +]; + +function TileLayer({ type }) { + switch(type) { + case "street": { + return ( + OpenStreetMap contributors` + } + url="http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" + maxZoom={19} + /> + ); + } + + case "terrain": { + return ( + + ); + } + + case "satellite": { + return ( + + ); + } + + case "dark": { + return ( + OpenStreetMap + © CartoDB + `} + subdomains="abcd" + maxZoom={19} + /> + ); + } + + case "monolight": { + return ( + + ); + } + } + + return ( + + ); +} + +export default TileLayer; diff --git a/resources/modules/editor/src/js/components/widgets/LocationWidget.js b/resources/modules/editor/src/js/components/widgets/LocationWidget.js new file mode 100644 index 000000000..faa883281 --- /dev/null +++ b/resources/modules/editor/src/js/components/widgets/LocationWidget.js @@ -0,0 +1,91 @@ +import clsx from "clsx"; +import AltrpLocation from "../altrp-location/AltrpLocation"; + +(window.globalDefaults = window.globalDefaults || []).push` + .altrp-location { + position: relative; + width: 100%; + overflow: hidden; + + &__preloader { + position: absolute; + height: 100%; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0.6); + z-index: 9999; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + + img { + display: inline-block; + width: 64px; + height: 64px; + } + } + + &__container { + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + } + } + + .leaflet-marker-icon { + &.leaflet-div-icon:not(.leaflet-editing-icon) { + background: transparent; + border: none; + } + } + + .leaflet-bottom.leaflet-right { + display: none; + } +`; + +class LocationWidget extends Component { + constructor(props) { + super(props); + + this.state = { + settings: props.element.getSettings(), + }; + + props.element.component = this; + + if (window.elementDecorator) { + window.elementDecorator(this); + } + + if(props.baseRender){ + this.render = props.baseRender(this); + } + } + + render() { + let classes = clsx( + { + active: this.isActive(), + "state-disabled": this.isDisabled(), + }, + this.props.element.getResponsiveLockedSetting("position_css_classes", "", "") + ); + + return ( + + ); + } +} + +export default LocationWidget; diff --git a/resources/modules/editor/src/js/components/widgets/styled-components/LocationComponent.js b/resources/modules/editor/src/js/components/widgets/styled-components/LocationComponent.js new file mode 100644 index 000000000..a131f01c8 --- /dev/null +++ b/resources/modules/editor/src/js/components/widgets/styled-components/LocationComponent.js @@ -0,0 +1,31 @@ +import {styledString} from "../../../../../../front-app/src/js/helpers/styles"; + +export default function LocationComponent(settings) { + const styles = [ + "altrp-image", + ["height", "style_height", "slider"], + "}", + + "altrp-btn", + ["margin", "style_margin", "dimensions"], + "}", + + ".state-disabled", + ["height", "style_height", "slider", ".state-disabled"], + "}", + + ".state-disabled", + ["margin", "style_margin", "dimensions", ".state-disabled"], + "}", + + ".active", + ["height", "style_height", "slider", ".active"], + "}", + + ".active", + ["margin", "style_margin", "dimensions", ".active"], + "}" + ]; + + return styledString(styles, settings); +}; diff --git a/resources/modules/editor/src/js/store/widgets/defaultState.js b/resources/modules/editor/src/js/store/widgets/defaultState.js index 7dfa2cc64..962aa8f3f 100644 --- a/resources/modules/editor/src/js/store/widgets/defaultState.js +++ b/resources/modules/editor/src/js/store/widgets/defaultState.js @@ -49,6 +49,7 @@ import AccordionWidget from "../../components/widgets/AccordionWidget/AccordionW import CarouselWidget from "../../components/widgets/CarouselWidget"; import MapWidget from "../../components/widgets/MapWidget"; import MapConstructorWidget from "../../components/widgets/MapConstructorWidget"; +import LocationWidget from "../../components/widgets/LocationWidget"; import DashboardsWidget from "../../components/widgets/DashboardsWidget"; import GalleryWidget from "../../components/widgets/GalleryWidget"; import Carousel from "../../classes/elements/Carousel"; @@ -62,6 +63,7 @@ import Table from "../../classes/elements/Table"; import Template from "../../classes/elements/Template"; import Posts from "../../classes/elements/Posts"; import Map from "../../classes/elements/Map"; +import Location from "../../classes/elements/Location"; import Menu from "../../classes/elements/Menu"; import MapConstructor from "../../classes/elements/MapConstructor"; import Dashboards from "../../classes/elements/Dashboards"; @@ -175,6 +177,7 @@ elements[Posts.getName()] = Posts; elements[Gallery.getName()] = Gallery; elements[Carousel.getName()] = Carousel; elements[Map.getName()] = Map; +elements[Location.getName()] = Location; elements[ImageLightbox.getName()] = ImageLightbox; elements[Dropbar.getName()] = Dropbar; elements[Scheduler.getName()] = Scheduler; @@ -248,6 +251,7 @@ components[Accordion.getName()] = AccordionWidget; components[Carousel.getName()] = CarouselWidget; components[Map.getName()] = MapWidget; components[MapConstructor.getName()] = MapConstructorWidget; +components[Location.getName()] = LocationWidget; components[Menu.getName()] = MenuWidget; components[ActionTrigger.getName()] = ActionTriggerWidget; diff --git a/resources/modules/editor/src/svgs/location.svg b/resources/modules/editor/src/svgs/location.svg new file mode 100644 index 000000000..af59ef68d --- /dev/null +++ b/resources/modules/editor/src/svgs/location.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/resources/modules/front-app/src/js/components/GlobalStyles.js b/resources/modules/front-app/src/js/components/GlobalStyles.js index aaa35e62d..6ce46f491 100644 --- a/resources/modules/front-app/src/js/components/GlobalStyles.js +++ b/resources/modules/front-app/src/js/components/GlobalStyles.js @@ -26,6 +26,7 @@ import getRouteStyles from "../functions/get-route-styles"; import MapComponent from "../../../../editor/src/js/components/widgets/styled-components/MapComponent"; import MapConstructorComponent from "../../../../editor/src/js/components/widgets/styled-components/MapConstructorComponent"; +import LocationComponent from "../../../../editor/src/js/components/widgets/styled-components/LocationComponent"; import TabsSwitcherComponent from "../../../../editor/src/js/components/widgets/styled-components/TabsSwitcherComponent"; import DiagramComponent from "../../../../editor/src/js/components/widgets/styled-components/DiagramComponent"; @@ -305,6 +306,9 @@ const GlobalStyles = createGlobalStyle`${({ elementsSettings, areas, globalCssEd case "map_builder": styles += `.${prefix}${id} {${MapConstructorComponent(item.settings)}}`; break; + case "location": + styles += `.${prefix}${id} {${LocationComponent(item.settings)}}`; + break; case "scheduler": styles += `.${prefix}${id} {${getSchedulerStyles(item.settings, id)}}`; break; diff --git a/resources/modules/front-app/src/js/constants/DEFAULT_REACT_ELEMENTS.js b/resources/modules/front-app/src/js/constants/DEFAULT_REACT_ELEMENTS.js index b07e18612..160ae10e1 100644 --- a/resources/modules/front-app/src/js/constants/DEFAULT_REACT_ELEMENTS.js +++ b/resources/modules/front-app/src/js/constants/DEFAULT_REACT_ELEMENTS.js @@ -29,6 +29,7 @@ const DEFAULT_REACT_ELEMENTS = [ 'breadcrumbs', 'map', 'map_builder', + 'location', 'menu', 'pie-diagram', 'line-diagram', diff --git a/resources/modules/front-app/src/js/constants/SKELETON_ELEMENTS.js b/resources/modules/front-app/src/js/constants/SKELETON_ELEMENTS.js index c4f04587e..63005d00d 100644 --- a/resources/modules/front-app/src/js/constants/SKELETON_ELEMENTS.js +++ b/resources/modules/front-app/src/js/constants/SKELETON_ELEMENTS.js @@ -28,6 +28,7 @@ const SKELETON_ELEMENTS = [ 'carousel', 'map', 'map_builder', + 'location', 'menu', 'pie-diagram', 'line-diagram', diff --git a/resources/modules/front-app/src/js/store/front-elements-store/defaultState.js b/resources/modules/front-app/src/js/store/front-elements-store/defaultState.js index 0c798bffb..b782727fb 100644 --- a/resources/modules/front-app/src/js/store/front-elements-store/defaultState.js +++ b/resources/modules/front-app/src/js/store/front-elements-store/defaultState.js @@ -264,6 +264,12 @@ export const defaultState = [ return await import(/* webpackChunkName: 'MapConstructorWidget' */ "../../../../../editor/src/js/components/widgets/MapConstructorWidget"); } }, + { + name: "location", + import: async () => { + return await import(/* webpackChunkName: 'LocationWidget' */ "../../../../../editor/src/js/components/widgets/LocationWidget"); + } + }, { name: "menu", import: async () => {