Sidebar
vanilla.html
react.html
vue.html
variables.ts
common.ts
common.css
<!DOCTYPE html>
<html>
<head>
<title>Vanilla example ymaps3-drawer-control</title>
<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"
src="../variables.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
import {LOCATION} from '../variables';
import {
subscribePositionClick,
subscribeVerticalTriggerPositionClick,
subscribeHorizontalTriggerPositionClick
} from './common';
window.map = null;
main();
async function main() {
await ymaps3.ready;
const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer} = ymaps3;
const {YMapDrawerControl} = await ymaps3.import('@yandex/ymaps3-drawer-control');
map = new YMap(document.getElementById('app'), {location: LOCATION}, [
new YMapDefaultSchemeLayer({}),
new YMapDefaultFeaturesLayer({})
]);
let open = true;
const drawerControl = new YMapDrawerControl({
position: 'left',
open,
onOpenChange: (value: boolean) => {
open = !value;
drawerControl.update({open});
},
content: () => {
const container = document.createElement('div');
container.classList.add('drawer-content');
const title = document.createElement('h1');
title.textContent = 'Good day, Sir!';
container.appendChild(title);
const text = document.createElement('span');
text.textContent = 'I am a sidebar :)';
container.appendChild(text);
return container;
}
});
subscribePositionClick((position) => drawerControl.update({position}));
subscribeVerticalTriggerPositionClick((verticalTriggerPosition) => drawerControl.update({verticalTriggerPosition}));
subscribeHorizontalTriggerPositionClick((horizontalTriggerPosition) =>
drawerControl.update({horizontalTriggerPosition})
);
map.addChild(drawerControl);
}
</script>
<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 class="container">
<div id="app"></div>
<div class="control">
<div class="positions-control">
<button id="position-top" class="positions top"></button>
<button id="position-bottom" class="positions bottom"></button>
<button id="position-left" class="positions left active"></button>
<button id="position-right" class="positions right"></button>
<div id="vertical-container" class="vertical left">
<button id="vp-top" class="circle"></button>
<button id="vp-center" class="circle active"></button>
<button id="vp-bottom" class="circle"></button>
</div>
<div id="horizontal-container" class="horizontal">
<button id="hp-left" class="circle"></button>
<button id="hp-center" class="circle active"></button>
<button id="hp-right" class="circle"></button>
</div>
</div>
<div>You can change position of sidebar and control for opening</div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>React example ymaps3-drawer-control</title>
<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="typescript"
type="text/babel"
src="./common.ts"
></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="../variables.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="react, typescript" type="text/babel">
import {HorizontalTriggerPosition, Position, VerticalTriggerPosition} from '@yandex/ymaps3-drawer-control';
import {LOCATION} from '../variables';
import {
subscribePositionClick,
subscribeHorizontalTriggerPositionClick,
subscribeVerticalTriggerPositionClick
} from './common';
window.map = null;
main();
async function main() {
const [ymaps3React] = await Promise.all([ymaps3.import('@yandex/ymaps3-reactify'), ymaps3.ready]);
const reactify = ymaps3React.reactify.bindTo(React, ReactDOM);
const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer} = reactify.module(ymaps3);
const {useState, useCallback, useEffect} = React;
const {YMapDrawerControl} = reactify.module(await ymaps3.import('@yandex/ymaps3-drawer-control'));
const App = () => {
const [open, setOpen] = useState(true);
const [position, setPosition] = useState<Position>('left');
const [verticalTriggerPosition, setVerticalTriggerPosition] = useState<VerticalTriggerPosition>('center');
const [horizontalTriggerPosition, setHorizontalTriggerPosition] = useState<HorizontalTriggerPosition>('center');
useEffect(() => {
subscribePositionClick((position) => setPosition(position));
subscribeVerticalTriggerPositionClick((position) => setVerticalTriggerPosition(position));
subscribeHorizontalTriggerPositionClick((position) => setHorizontalTriggerPosition(position));
}, []);
const content = useCallback(
() => (
<div className="drawer-content">
<h1>Good day, Sir!</h1>
<span>I am a sidebar :)</span>
</div>
),
[]
);
const onOpenChange = (value: boolean) => {
setOpen(!value);
};
return (
<YMap location={LOCATION} ref={(x) => (map = x)}>
<YMapDefaultSchemeLayer />
<YMapDefaultFeaturesLayer />
<YMapDrawerControl
position={position}
verticalTriggerPosition={verticalTriggerPosition}
horizontalTriggerPosition={horizontalTriggerPosition}
content={content}
open={open}
onOpenChange={onOpenChange}
/>
</YMap>
);
};
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('app')
);
}
</script>
<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 class="container">
<div id="app"></div>
<div class="control">
<div class="positions-control">
<button id="position-top" class="positions top"></button>
<button id="position-bottom" class="positions bottom"></button>
<button id="position-left" class="positions left active"></button>
<button id="position-right" class="positions right"></button>
<div id="vertical-container" class="vertical left">
<button id="vp-top" class="circle"></button>
<button id="vp-center" class="circle active"></button>
<button id="vp-bottom" class="circle"></button>
</div>
<div id="horizontal-container" class="horizontal">
<button id="hp-left" class="circle"></button>
<button id="hp-center" class="circle active"></button>
<button id="hp-right" class="circle"></button>
</div>
</div>
<div>You can change position of sidebar and control for opening</div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Vue example ymaps3-drawer-control</title>
<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>
<!-- 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"
src="../variables.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
import {LOCATION} from '../variables';
import {Position, VerticalTriggerPosition, HorizontalTriggerPosition} from '@yandex/ymaps3-drawer-control';
import {
subscribePositionClick,
subscribeHorizontalTriggerPositionClick,
subscribeVerticalTriggerPositionClick
} from './common';
window.map = null;
main();
async function main() {
const [ymaps3Vue] = await Promise.all([ymaps3.import('@yandex/ymaps3-vuefy'), ymaps3.ready]);
const vuefy = ymaps3Vue.vuefy.bindTo(Vue);
const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer} = vuefy.module(ymaps3);
const {YMapDrawerControl} = vuefy.module(await ymaps3.import('@yandex/ymaps3-drawer-control'));
const app = Vue.createApp({
components: {
YMap,
YMapDefaultSchemeLayer,
YMapDefaultFeaturesLayer,
YMapDrawerControl
},
setup() {
const refMap = (ref) => {
window.map = ref?.entity;
};
const open = Vue.ref(true);
const position = Vue.ref<Position>('left');
const verticalTriggerPosition = Vue.ref<VerticalTriggerPosition>('center');
const horizontalTriggerPosition = Vue.ref<HorizontalTriggerPosition>('center');
subscribePositionClick((newPosition) => {
position.value = newPosition;
});
subscribeHorizontalTriggerPositionClick((position) => {
horizontalTriggerPosition.value = position;
});
subscribeVerticalTriggerPositionClick((position) => {
verticalTriggerPosition.value = position;
});
const onOpenChange = (value: boolean) => {
open.value = !value;
};
return {LOCATION, refMap, open, position, verticalTriggerPosition, horizontalTriggerPosition, onOpenChange};
},
template: `
<YMap :location="LOCATION" :ref="refMap">
<YMapDefaultSchemeLayer />
<YMapDefaultFeaturesLayer />
<YMapDrawerControl
:onOpenChange="onOpenChange"
:position="position"
:verticalTriggerPosition="verticalTriggerPosition"
:horizontalTriggerPosition="horizontalTriggerPosition"
:open="open">
<template #content>
<div class="drawer-content">
<h1>Good day, Sir!</h1>
<span>I am a sidebar :)</span>
</div>
</template>
</YMapDrawerControl>
</YMap>`
});
app.mount('#app');
}
</script>
<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 class="container">
<div id="app"></div>
<div class="control">
<div class="positions-control">
<button id="position-top" class="positions top"></button>
<button id="position-bottom" class="positions bottom"></button>
<button id="position-left" class="positions left active"></button>
<button id="position-right" class="positions right"></button>
<div id="vertical-container" class="vertical left">
<button id="vp-top" class="circle"></button>
<button id="vp-center" class="circle active"></button>
<button id="vp-bottom" class="circle"></button>
</div>
<div id="horizontal-container" class="horizontal">
<button id="hp-left" class="circle"></button>
<button id="hp-center" class="circle active"></button>
<button id="hp-right" class="circle"></button>
</div>
</div>
<div>You can change position of sidebar and control for opening</div>
</div>
</div>
</body>
</html>
import type {YMapLocationRequest} from '@yandex/ymaps3-types';
export const LOCATION: YMapLocationRequest = {
center: [37.623082, 55.75254], // starting position [lng, lat]
zoom: 9 // starting zoom
};
import {Position, VerticalTriggerPosition, HorizontalTriggerPosition} from '@yandex/ymaps3-drawer-control';
ymaps3.import.registerCdn('https://cdn.jsdelivr.net/npm/{package}', '@yandex/ymaps3-drawer-control@0.0');
// code for position control
type PositionCallback = (position: Position) => void;
type VerticalTriggerPositionCallback = (position: VerticalTriggerPosition) => void;
type HorizontalTriggerPositionCallback = (position: HorizontalTriggerPosition) => void;
const positionButtons: Record<Position, HTMLElement> = {
top: document.getElementById('position-top'),
bottom: document.getElementById('position-bottom'),
left: document.getElementById('position-left'),
right: document.getElementById('position-right')
};
const verticalContainer = document.getElementById('vertical-container');
const verticalTriggerPositionButtons: Record<VerticalTriggerPosition, HTMLElement> = {
top: document.getElementById('vp-top'),
center: document.getElementById('vp-center'),
bottom: document.getElementById('vp-bottom')
};
const horizontalContainer = document.getElementById('horizontal-container');
const horizontalTriggerPositionButtons: Record<HorizontalTriggerPosition, HTMLElement> = {
left: document.getElementById('hp-left'),
center: document.getElementById('hp-center'),
right: document.getElementById('hp-right')
};
const positionObservers: Array<PositionCallback> = [];
const verticalTriggerPositionObservers: Array<VerticalTriggerPositionCallback> = [];
const horizontalTriggerPositionObservers: Array<HorizontalTriggerPositionCallback> = [];
Object.keys(positionButtons).forEach((position: Position) => {
positionButtons[position].addEventListener('click', () => {
positionObservers.forEach((cb) => cb(position));
Object.keys(positionButtons).forEach((key: Position) =>
positionButtons[key].classList.toggle('active', key === position)
);
verticalContainer.classList.toggle('left', position === 'left');
verticalContainer.classList.toggle('right', position === 'right');
horizontalContainer.classList.toggle('top', position === 'top');
horizontalContainer.classList.toggle('bottom', position === 'bottom');
});
});
Object.keys(verticalTriggerPositionButtons).forEach((position: VerticalTriggerPosition) => {
verticalTriggerPositionButtons[position].addEventListener('click', () => {
verticalTriggerPositionObservers.forEach((cb) => cb(position));
Object.keys(verticalTriggerPositionButtons).forEach((key: VerticalTriggerPosition) =>
verticalTriggerPositionButtons[key].classList.toggle('active', key === position)
);
});
});
Object.keys(horizontalTriggerPositionButtons).forEach((position: HorizontalTriggerPosition) => {
horizontalTriggerPositionButtons[position].addEventListener('click', () => {
horizontalTriggerPositionObservers.forEach((cb) => cb(position));
Object.keys(horizontalTriggerPositionButtons).forEach((key: HorizontalTriggerPosition) =>
horizontalTriggerPositionButtons[key].classList.toggle('active', key === position)
);
});
});
export const subscribePositionClick = (cb: PositionCallback) => {
positionObservers.push(cb);
};
export const subscribeVerticalTriggerPositionClick = (cb: VerticalTriggerPositionCallback) => {
verticalTriggerPositionObservers.push(cb);
};
export const subscribeHorizontalTriggerPositionClick = (cb: HorizontalTriggerPositionCallback) => {
horizontalTriggerPositionObservers.push(cb);
};
.drawer-content {
padding: 20px;
font-size: 20px;
font-weight: 500;
font-style: normal;
line-height: 24px;
color: #050d33;
}
.drawer-content h1 {
margin: 0 0 10px;
font-size: 32px;
line-height: 36px;
}
.container {
--button-width: 24px;
display: flex;
flex-direction: row;
width: 100%;
height: 100%;
gap: 8px;
}
.container #app {
border: 1px solid rgba(92, 94, 102, 0.14);
border-radius: 8px;
}
.control {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 200px;
padding: 16px;
font-size: 12px;
font-style: normal;
line-height: 16px;
color: #7b7d85;
border: 1px solid rgba(92, 94, 102, 0.14);
border-radius: 8px;
background: #fff;
}
.positions-control {
position: relative;
height: 140px;
border-radius: 8px;
background: #fff;
}
button {
margin: 0;
padding: 0;
border: none;
outline: none;
background: none;
}
button.positions {
position: absolute;
cursor: pointer;
border: 1px solid #5c5e6624;
border-radius: 8px;
}
button.positions.active,
button.positions:hover {
background-color: #122db20a;
}
button.top {
top: 0;
width: 100%;
height: var(--button-width);
}
button.bottom {
bottom: 0;
width: 100%;
height: var(--button-width);
}
button.left {
left: 0;
width: var(--button-width);
height: 100%;
}
button.right {
right: 0;
width: var(--button-width);
height: 100%;
}
button.circle {
display: block;
width: 16px;
height: 16px;
border: 2px solid #fff;
border-radius: 8px;
background-color: #e4e6eb;
}
button.circle.active {
background-color: #34374a;
}
button.circle.active:hover {
border-color: #34374a;
}
button.circle:hover {
border-color: #e4e6eb;
}
.vertical {
position: absolute;
display: none;
flex-direction: column;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
width: 16px;
height: 100%;
padding: 32px 0;
}
.vertical.left {
left: 30px;
display: flex;
}
.vertical.right {
right: 30px;
display: flex;
}
.horizontal {
position: absolute;
display: none;
flex-direction: row;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
width: 100%;
height: 16px;
padding: 0 32px;
}
.horizontal.top {
top: 30px;
display: flex;
}
.horizontal.bottom {
bottom: 30px;
display: flex;
}