Создание маркера с попап окном

Open on CodeSandbox

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
    <script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
    <!-- To make the map appear, you must add your apikey -->
    <script src="https://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>

    <script
      data-plugins="transform-modules-umd"
      data-presets="typescript"
      type="text/babel"
      src="./common.ts"
    ></script>
    <script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
      import type {LngLat, YMapMarker} from '@yandex/ymaps3-types';
      import {LOCATION, LONG_TEXT} from './common';

      window.map = null;

      main();
      async function main() {
          // Waiting for all api elements to be loaded
          await ymaps3.ready;
          const {YMapComplexEntity, YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker} = ymaps3;

          // Import the package to add a default marker
          const {YMapDefaultMarker} = await ymaps3.import('@yandex/ymaps3-markers@0.0.1');

          // Create a custom marker class with a popup
          interface CustomMarkerWithPopupProps {
              coordinates: LngLat; // marker position [lng, lat]
              popupContent: string;
              zIndex?: number;
              blockBehaviors?: boolean;
          }
          class CustomMarkerWithPopup extends YMapComplexEntity<CustomMarkerWithPopupProps> {
              private _marker: YMapMarker;
              private _popup: YMapMarker | null = null;

              // Handler for attaching the control to the map
              _onAttach() {
                  this._createMarker();
              }
              // Handler for detaching control from the map
              _onDetach() {
                  this._marker = null;
              }
              // Handler for updating marker properties
              _onUpdate(props: CustomMarkerWithPopupProps) {
                  if (props.zIndex !== undefined) {
                      this._marker?.update({zIndex: props.zIndex});
                  }
                  if (props.coordinates !== undefined) {
                      this._marker?.update({coordinates: props.coordinates});
                  }
              }
              // Method for creating a marker element
              _createMarker() {
                  const element = document.createElement('div');
                  element.className = 'marker';
                  element.onclick = () => {
                      this._openPopup();
                  };

                  this._marker = new YMapMarker({coordinates: this._props.coordinates}, element);
                  this.addChild(this._marker);
              }

              // Method for creating a popup window element
              _openPopup() {
                  if (this._popup) {
                      return;
                  }

                  const element = document.createElement('div');
                  element.className = 'popup';

                  const textElement = document.createElement('div');
                  textElement.className = 'popup__text';
                  textElement.textContent = this._props.popupContent;

                  const closeBtn = document.createElement('button');
                  closeBtn.className = 'popup__close';
                  closeBtn.textContent = 'Close Popup';
                  closeBtn.onclick = () => this._closePopup();

                  element.append(textElement, closeBtn);

                  const zIndex = (this._props.zIndex ?? YMapMarker.defaultProps.zIndex) + 1_000;
                  this._popup = new YMapMarker({
                      coordinates: this._props.coordinates,
                      zIndex,
                      // This allows you to scroll over popup
                      blockBehaviors: this._props.blockBehaviors
                  }, element);
                  this.addChild(this._popup);
              }

              _closePopup() {
                  if (!this._popup) {
                      return;
                  }

                  this.removeChild(this._popup);
                  this._popup = null;
              }
          }

          // Initialize the map
          map = new YMap(
              // Pass the link to the HTMLElement of the container
              document.getElementById('app'),
              // Pass the map initialization parameters
              {location: LOCATION, showScaleInCopyrights: true},
              [
                  // Add a map scheme layer
                  new YMapDefaultSchemeLayer({}),
                  // Add a layer of geo objects to display the markers
                  new YMapDefaultFeaturesLayer({})
              ]
          );

          map
              // Add a default marker with a popup window from the package to the map
              .addChild(
                  new YMapDefaultMarker({
                      coordinates: [37.9, 55.85],
                      color: '#006efc',
                      popup: {content: 'Popup on the default marker', position: 'left'}
                  })
              )
              // Add a custom marker with a popup window to the map
              .addChild(new CustomMarkerWithPopup({coordinates: [37.32, 55.57], popupContent: 'Popup on the custom marker (not scrollable)'}))
              .addChild(new CustomMarkerWithPopup({coordinates: [37.74, 55.43], popupContent: LONG_TEXT, blockBehaviors: true}));
      }
    </script>

    <!-- prettier-ignore -->
    <style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; }  </style>
    <link rel="stylesheet" href="./common.css" />
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
    <script crossorigin src="https://cdn.jsdelivr.net/npm/react@17/umd/react.production.min.js"></script>
    <script crossorigin src="https://cdn.jsdelivr.net/npm/react-dom@17/umd/react-dom.production.min.js"></script>
    <script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
    <!-- To make the map appear, you must add your apikey -->
    <script src="https://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>

    <script
      data-plugins="transform-modules-umd"
      data-presets="react, typescript"
      type="text/babel"
      src="./common.ts"
    ></script>
    <script data-plugins="transform-modules-umd" data-presets="react, typescript" type="text/babel">
      import type {LngLat} from '@yandex/ymaps3-types';
      import {LOCATION, LONG_TEXT} from './common';

      window.map = null;

      main();
      async function main() {
        // For each object in the JS API, there is a React counterpart
        // To use the React version of the API, include the module @yandex/ymaps3-reactify
        const [ymaps3React] = await Promise.all([ymaps3.import('@yandex/ymaps3-reactify'), ymaps3.ready]);
        const reactify = ymaps3React.reactify.bindTo(React, ReactDOM);
        const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker} = reactify.module(ymaps3);
        const {useState} = React;

        // Import the package to add a default marker
        const {YMapDefaultMarker} = reactify.module(await ymaps3.import('@yandex/ymaps3-markers@0.0.1'));

        function App() {
          return (
            // Initialize the map and pass initialization parameters
            <YMap location={LOCATION} showScaleInCopyrights={true} ref={(x) => (map = x)}>
              {/* Add a map scheme layer */}
              <YMapDefaultSchemeLayer />
              {/* Add a layer of geo objects to display the markers */}
              <YMapDefaultFeaturesLayer />

              {/* Add a default marker with a popup window from the package to the map */}
              <YMapDefaultMarker
                coordinates={[37.9, 55.85]}
                color="#006efc"
                popup={{content: 'Popup on the default marker', position: 'left'}}
              />
              {/* Add a custom marker with a popup window to the map */}
              <CustomMarkerWithPopup
                coordinates={[37.32, 55.57]}
                popupContent="Popup on the custom marker (not scrollable)"
              />
              <CustomMarkerWithPopup coordinates={[37.74, 55.43]} popupContent={LONG_TEXT} blockBehaviors />
            </YMap>
          );
        }

        // Create a custom marker component with a popup
        interface CustomMarkerWithPopupProps {
          coordinates: LngLat; // marker position [lng, lat]
          popupContent: string;
          zIndex?: number;
          blockBehaviors?: boolean;
        }

        function CustomMarkerWithPopup(props: CustomMarkerWithPopupProps) {
          const [popupOpen, setPopupOpen] = useState(false);

          return (
            <>
              <YMapMarker coordinates={props.coordinates}>
                <div className="marker" onClick={() => setPopupOpen(true)}></div>
              </YMapMarker>

              {/**
               * Create or delete a popup using conditional rendering.
               * `YMapMarker.blockBehaviors` prop allows you to scroll over popup.
               */}
              {popupOpen && (
                <YMapMarker
                  coordinates={props.coordinates}
                  zIndex={(props.zIndex ?? ymaps3.YMapMarker.defaultProps.zIndex) + 1_000}
                  blockBehaviors={props.blockBehaviors}
                >
                  <div className="popup">
                    <div className="popup__text">{props.popupContent}</div>
                    <button className="popup__close" onClick={() => setPopupOpen(false)}>
                      Close Popup
                    </button>
                  </div>
                </YMapMarker>
              )}
            </>
          );
        }

        ReactDOM.render(
          <React.StrictMode>
            <App />
          </React.StrictMode>,
          document.getElementById('app')
        );
      }
    </script>

    <!-- prettier-ignore -->
    <style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; }  </style>
    <link rel="stylesheet" href="./common.css" />
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
    <script crossorigin src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
    <script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>

    <script src="https://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>

    <script
      data-plugins="transform-modules-umd"
      data-presets="typescript"
      type="text/babel"
      src="./common.ts"
    ></script>
    <script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
      import type {LngLat} from '@yandex/ymaps3-types';
      import {LOCATION, LONG_TEXT} from './common';

      window.map = null;

      async function main() {
        // For each object in the JS API, there is a Vue counterpart
        // To use the Vue version of the API, include the module @yandex/ymaps3-vuefy
        const [ymaps3Vue] = await Promise.all([ymaps3.import('@yandex/ymaps3-vuefy'), ymaps3.ready]);
        const vuefy = ymaps3Vue.vuefy.bindTo(Vue);
        const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker} = vuefy.module(ymaps3);

        // Import the package to add a default marker
        const {YMapDefaultMarker} = vuefy.module(await ymaps3.import('@yandex/ymaps3-markers@0.0.1'));

        const CustomMarkerWithPopup = Vue.defineComponent({
          setup(props) {
            const popupOpen = Vue.ref(false);
            const togglePopup = () => {
              popupOpen.value = !popupOpen.value;
            };
            const zIndexCmp = Vue.computed(() => (props.zIndex ?? ymaps3.YMapMarker.defaultProps.zIndex) + 1_000);

            return {popupOpen, togglePopup, zIndexCmp};
          },
          props: {
            coordinates: Array,
            popupContent: String,
            zIndex: Number,
            blockBehaviors: Boolean
          },
          components: {YMapMarker},
          template: `
          <YMapMarker :coordinates="coordinates">
            <!-- Create or delete a popup using conditional rendering  -->
            <div class="marker" @click="togglePopup"></div>
          </YMapMarker>
          <YMapMarker v-if="popupOpen" :coordinates="coordinates" :zIndex="zIndexCmp" :blockBehaviors="blockBehaviors">
            <div class="popup">
              <div class="popup__text">{{ popupContent }}</div>
              <button class="popup__close" @click="togglePopup">Close Popup</button>
            </div>
          </YMapMarker>
        `
        });

        const app = Vue.createApp({
          components: {
            YMap,
            YMapDefaultSchemeLayer,
            YMapDefaultFeaturesLayer,
            YMapMarker,
            YMapDefaultMarker,
            CustomMarkerWithPopup
          },
          setup() {
            const refMap = (ref) => {
              window.map = ref?.entity;
            };
            return {refMap, LOCATION, LONG_TEXT};
          },
          template: `
          <YMap :location="LOCATION" :showScaleInCopyrights="true" :ref="refMap">
            <!-- Add a map scheme layer -->
            <YMapDefaultSchemeLayer/>
            <!-- Add a layer of geo objects to display the markers -->
            <YMapDefaultFeaturesLayer/>

            <!-- Add a default marker with a popup window from the package to the map -->
            <YMapDefaultMarker
                :coordinates="[37.9, 55.85]"
                color="#006efc"
                :popup="{content: 'Popup on the default marker', position: 'left'}"
            />
            <!-- Add a custom marker with a popup window to the map -->
            <CustomMarkerWithPopup :coordinates="[37.32, 55.57]" popupContent="Popup on the custom marker (not scrollable)"/>
            <CustomMarkerWithPopup :coordinates="[37.74, 55.43]" :popupContent="LONG_TEXT" blockBehaviors />
          </YMap>
        `
        });
        app.mount('#app');
      }
      main();
    </script>

    <!-- prettier-ignore -->
    <style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; }  </style>
    <link rel="stylesheet" href="./common.css" />
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
.marker {
  width: 25px;
  height: 25px;

  cursor: pointer;

  border: 10px solid #006efc;
  border-radius: 50%;
  background-color: #fff;

  transform: translate(-50%, -50%);
}

.popup {
  position: absolute;

  display: flex;
  overflow: scroll;
  flex-direction: column;
  align-items: center;

  width: 150px;
  max-height: 150px;
  padding: 20px 25px;

  border: 2px solid #000;
  border-radius: 7px;
  background: #fff;
  box-shadow: 0 0 8px 0 #0003;

  transform: translate(30px, -220px);

  gap: 20px;

  overscroll-behavior: contain;
  touch-action: pan-y;
}

.popup__text {
  font-size: 20px;
  font-weight: 600;
  white-space: break-spaces;
}

.popup__close {
  padding: 7px;

  font-size: 16px;
  cursor: pointer;

  color: #fff;
  border: none;
  border-radius: 7px;
  outline: none;
  background-color: rgb(253, 100, 102);

  transition: background-color 0.2s;
}

.popup__close:hover {
  background-color: rgb(253, 80, 80);
}

.popup__close:active {
  background-color: rgb(253, 100, 102);
}
import type {YMapLocationRequest} from '@yandex/ymaps3-types';

export const LOCATION: YMapLocationRequest = {
  center: [37.623082, 55.75254], // starting position [lng, lat]
  zoom: 9 // starting zoom
};

export const LONG_TEXT =
  'Popup on the custom marker (scrollable)\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';