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 `
+
+ `
+}
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 () => {