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} } };