Боковая панель

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