diff --git a/example/marker-popup/common.css b/example/marker-popup/common.css
new file mode 100644
index 0000000..e69de29
diff --git a/example/marker-popup/common.ts b/example/marker-popup/common.ts
new file mode 100644
index 0000000..835e6ef
--- /dev/null
+++ b/example/marker-popup/common.ts
@@ -0,0 +1,5 @@
+import type {LngLat, MMapLocationRequest} from '@mappable-world/mappable-types';
+
+export const CENTER: LngLat = [55.442795, 25.24107];
+
+export const LOCATION: MMapLocationRequest = {center: CENTER, zoom: 9};
diff --git a/example/marker-popup/react/index.html b/example/marker-popup/react/index.html
new file mode 100644
index 0000000..4183912
--- /dev/null
+++ b/example/marker-popup/react/index.html
@@ -0,0 +1,37 @@
+
+
+
+ React example mappable-default-ui-theme
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/marker-popup/react/index.tsx b/example/marker-popup/react/index.tsx
new file mode 100644
index 0000000..84cfca8
--- /dev/null
+++ b/example/marker-popup/react/index.tsx
@@ -0,0 +1,49 @@
+import {MarkerPopupProps, MarkerSizeProps} from '../../src';
+import {CENTER, LOCATION} from '../common';
+
+window.map = null;
+
+main();
+async function main() {
+ const [mappableReact] = await Promise.all([mappable.import('@mappable-world/mappable-reactify'), mappable.ready]);
+ const reactify = mappableReact.reactify.bindTo(React, ReactDOM);
+
+ const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} =
+ reactify.module(mappable);
+
+ const {useState, useCallback} = React;
+
+ const {MMapDefaultMarker} = reactify.module(await mappable.import('@mappable-world/mappable-default-ui-theme'));
+
+ ReactDOM.render(
+
+
+ ,
+ document.getElementById('app')
+ );
+
+ function App() {
+ const [location] = useState(LOCATION);
+ const [size, setSize] = useState('normal');
+ const [popup] = useState({
+ title: 'Popup',
+ description: 'Description for this marker',
+ action: 'Action'
+ });
+
+ return (
+ (map = x)}>
+
+
+
+
+
+
+ setSize('normal'), [])} />
+ setSize('small'), [])} />
+ setSize('micro'), [])} />
+
+
+ );
+ }
+}
diff --git a/example/marker-popup/vanilla/index.html b/example/marker-popup/vanilla/index.html
new file mode 100644
index 0000000..730390e
--- /dev/null
+++ b/example/marker-popup/vanilla/index.html
@@ -0,0 +1,35 @@
+
+
+
+ Vanilla example mappable-default-ui-theme
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/marker-popup/vanilla/index.ts b/example/marker-popup/vanilla/index.ts
new file mode 100644
index 0000000..62fad3a
--- /dev/null
+++ b/example/marker-popup/vanilla/index.ts
@@ -0,0 +1,36 @@
+import {CENTER, LOCATION} from '../common';
+window.map = null;
+
+main();
+async function main() {
+ await mappable.ready;
+ const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = mappable;
+
+ const {MMapDefaultMarker} = await mappable.import('@mappable-world/mappable-default-ui-theme');
+
+ map = new MMap(document.getElementById('app'), {location: LOCATION});
+
+ map.addChild(new MMapDefaultSchemeLayer({}));
+ map.addChild(new MMapDefaultFeaturesLayer({}));
+
+ const marker = new MMapDefaultMarker({
+ coordinates: CENTER,
+ iconName: 'fallback',
+ size: 'normal',
+ popup: {
+ title: 'Popup',
+ description: 'Description for this marker',
+ action: 'Action'
+ }
+ });
+
+ map.addChild(marker);
+
+ map.addChild(
+ new MMapControls({position: 'top left'}, [
+ new MMapControlButton({text: 'Normal', onClick: () => marker.update({size: 'normal'})}),
+ new MMapControlButton({text: 'Small', onClick: () => marker.update({size: 'small'})}),
+ new MMapControlButton({text: 'Micro', onClick: () => marker.update({size: 'micro'})})
+ ])
+ );
+}
diff --git a/example/marker-popup/vue/index.html b/example/marker-popup/vue/index.html
new file mode 100644
index 0000000..85eeebd
--- /dev/null
+++ b/example/marker-popup/vue/index.html
@@ -0,0 +1,36 @@
+
+
+
+ Vue example mappable-default-ui-theme
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/marker-popup/vue/index.ts b/example/marker-popup/vue/index.ts
new file mode 100644
index 0000000..3ddd9d2
--- /dev/null
+++ b/example/marker-popup/vue/index.ts
@@ -0,0 +1,54 @@
+import {MarkerPopupProps, MarkerSizeProps} from '../../src';
+import {CENTER, LOCATION} from '../common';
+
+window.map = null;
+
+main();
+async function main() {
+ const [mappableVue] = await Promise.all([mappable.import('@mappable-world/mappable-vuefy'), mappable.ready]);
+ const vuefy = mappableVue.vuefy.bindTo(Vue);
+
+ const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} =
+ vuefy.module(mappable);
+
+ const {MMapDefaultMarker} = vuefy.module(await mappable.import('@mappable-world/mappable-default-ui-theme'));
+
+ const app = Vue.createApp({
+ components: {
+ MMap,
+ MMapDefaultSchemeLayer,
+ MMapDefaultFeaturesLayer,
+ MMapControls,
+ MMapControlButton,
+ MMapDefaultMarker
+ },
+ setup() {
+ const size = Vue.ref('normal');
+ const popup = Vue.ref({
+ title: 'Popup',
+ description: 'Description for this marker',
+ action: 'Action'
+ });
+ const refMap = (ref: any) => {
+ window.map = ref?.entity;
+ };
+ const setNormalSize = () => (size.value = 'normal');
+ const setSmallSize = () => (size.value = 'small');
+ const setMicroSize = () => (size.value = 'micro');
+
+ return {LOCATION, CENTER, size, popup, refMap, setNormalSize, setSmallSize, setMicroSize};
+ },
+ template: `
+
+
+
+
+
+
+
+
+
+ `
+ });
+ app.mount('#app');
+}
diff --git a/src/markers/MMapDefaultMarker/index.ts b/src/markers/MMapDefaultMarker/index.ts
index fcad78e..bce4993 100644
--- a/src/markers/MMapDefaultMarker/index.ts
+++ b/src/markers/MMapDefaultMarker/index.ts
@@ -1,6 +1,7 @@
import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types';
import {IconColor, IconName, iconColors, icons} from '../../icons';
import {MMapDefaultMarkerVuefyOptions} from './vue';
+import {MMapDefaultPopupMarker} from '../';
import microPoiStrokeSVG from './backgrounds/micro-poi-stroke.svg';
import microPoiSVG from './backgrounds/micro-poi.svg';
@@ -30,9 +31,21 @@ const HINT_SUBTITLE_CLASS = 'mappable--hint-subtitle';
const HINT_STABLE = 'mappable--hint__stable';
const HINT_HOVERED = 'mappable--hint__hovered';
+const DISTANCE_BETWEEN_POPUP_AND_MARKER = 8;
+
export type ThemesColor = {day: string; night: string};
export type MarkerColorProps = IconColor | ThemesColor;
export type MarkerSizeProps = 'normal' | 'small' | 'micro';
+export type MarkerPopupProps = {
+ /** Displayed title in popup header */
+ title?: string;
+ /** Displayed description */
+ description?: string;
+ /** The inscription on the action button */
+ action?: string;
+ /** Callback of click the action button */
+ onAction?: () => void;
+};
export type MMapDefaultMarkerProps = MMapMarkerProps & {
iconName?: IconName;
@@ -41,6 +54,7 @@ export type MMapDefaultMarkerProps = MMapMarkerProps & {
title?: string;
subtitle?: string;
staticHint?: boolean;
+ popup?: MarkerPopupProps;
};
const defaultProps = Object.freeze({color: 'darkgray', size: 'small', staticHint: true});
@@ -64,6 +78,8 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity this._updateTheme(), {
immediate: true
});
@@ -132,6 +163,11 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity {
+ this._popup.update({show: !this._popup.isOpen});
+ this._props.onClick?.(event);
+ };
+
private _updateTheme() {
const themeCtx = this._consumeContext(mappable.ThemeContext);
const theme = themeCtx.theme;
@@ -222,6 +263,23 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity = {
@@ -24,10 +24,12 @@ export const MMapDefaultMarkerVuefyOptions: CustomVuefyOptions,
onFastClick: Function as TVue.PropType,
iconName: {type: String as TVue.PropType},
- color: {type: Object as TVue.PropType, default: 'darkgray'},
+ // @ts-ignore CustomVuefyOptions types no support multiple types
+ color: {type: [Object as TVue.PropType, String], default: 'darkgray'},
size: {type: String as TVue.PropType, default: 'small'},
title: {type: String},
subtitle: {type: String},
- staticHint: {type: Boolean, default: true}
+ staticHint: {type: Boolean, default: true},
+ popup: {type: Object as TVue.PropType}
}
};