feat: [] init
Some checks failed
Merge release to pg / merge-release-to-pg (push) Waiting to run
Release Drafter / update_release_draft (push) Has been cancelled

This commit is contained in:
Nikita Kraev 2025-11-12 22:43:13 +04:00
parent 15113b4bbb
commit 5088bd113c
8 changed files with 414 additions and 0 deletions

View File

@ -142,6 +142,7 @@
"js-sha256": "^0.9.0",
"jshint": "^2.13.4",
"klona": "^2.0.5",
"leaflet": "^1.9.4",
"libphonenumber-js": "^1.9.44",
"linkedom": "^0.14.20",
"localforage": "^1.7.3",
@ -185,6 +186,7 @@
"react-helmet": "^5.2.1",
"react-hook-form": "^7.28.0",
"react-json-view": "^1.21.3",
"react-leaflet": "3.2.5",
"react-media-recorder": "^1.6.1",
"react-modal": "^3.15.1",
"react-page-visibility": "^7.0.0",

View File

@ -0,0 +1,158 @@
import React, { useEffect } from "react";
import type { WidgetProps } from "widgets/BaseWidget";
import styled from "styled-components";
import { MapContainer, TileLayer, Marker, Popup, useMap } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import L from "leaflet";
import { FullScreen, useFullScreenHandle } from "react-full-screen";
import { Icon } from "@blueprintjs/core";
// Контейнер для карты с относительным позиционированием
const MapWrapper = styled.div`
position: relative;
width: 100%;
height: 100%;
`;
// Кнопка для переключения полноэкранного режима
const FullscreenButton = styled.button`
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
background: white;
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: background-color 0.2s;
&:hover {
background-color: #f0f0f0;
}
&:active {
background-color: #e0e0e0;
}
`;
// 🧩 фикс, чтобы маркеры отображались (иначе пустые иконки)
delete (L.Icon.Default.prototype as any)._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
iconUrl: require("leaflet/dist/images/marker-icon.png"),
shadowUrl: require("leaflet/dist/images/marker-shadow.png"),
});
// Компонент для обновления размера карты при изменении размера контейнера
function MapResizer() {
const map = useMap();
useEffect(() => {
// Обновляем размер карты при монтировании
const timer = setTimeout(() => {
map.invalidateSize();
}, 100);
return () => clearTimeout(timer);
}, [map]);
// Обновляем размер при изменении размера окна
useEffect(() => {
const handleResize = () => {
map.invalidateSize();
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, [map]);
// Обновляем размер при переключении полноэкранного режима
useEffect(() => {
const handleFullscreenChange = () => {
// Небольшая задержка, чтобы контейнер успел изменить размер
setTimeout(() => {
map.invalidateSize();
}, 200);
};
document.addEventListener("fullscreenchange", handleFullscreenChange);
document.addEventListener("webkitfullscreenchange", handleFullscreenChange);
document.addEventListener("mozfullscreenchange", handleFullscreenChange);
document.addEventListener("MSFullscreenChange", handleFullscreenChange);
return () => {
document.removeEventListener("fullscreenchange", handleFullscreenChange);
document.removeEventListener("webkitfullscreenchange", handleFullscreenChange);
document.removeEventListener("mozfullscreenchange", handleFullscreenChange);
document.removeEventListener("MSFullscreenChange", handleFullscreenChange);
};
}, [map]);
return null;
}
function OpenStreetMapComponent(props: OpenStreetMapComponentProps) {
// Получаем значения из props или используем значения по умолчанию
const centerLat = props.centerLat ?? 55.751244;
const centerLng = props.centerLng ?? 37.618423;
const zoom = props.zoom ?? 7;
const markerLat = props.markerLat ?? centerLat;
const markerLng = props.markerLng ?? centerLng;
const markerText = props.markerText ?? "Привет! Это Москва 🏙️";
const center: [number, number] = [centerLat, centerLng];
const markerPosition: [number, number] = [markerLat, markerLng];
// Хук для управления полноэкранным режимом
const fullScreenHandle = useFullScreenHandle();
return (
<MapWrapper>
<FullScreen handle={fullScreenHandle}>
<MapContainer
center={center}
zoom={zoom}
style={{ height: "500px", width: "500px" }}
>
<MapResizer />
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors'
/>
<Marker position={markerPosition}>
<Popup>{markerText}</Popup>
</Marker>
</MapContainer>
</FullScreen>
{/* Кнопка для переключения полноэкранного режима */}
<FullscreenButton
onClick={fullScreenHandle.active ? fullScreenHandle.exit : fullScreenHandle.enter}
title={fullScreenHandle.active ? "Выйти из полноэкранного режима" : "Развернуть на весь экран"}
>
<Icon
icon={fullScreenHandle.active ? "minimize" : "maximize"}
iconSize={16}
/>
</FullscreenButton>
</MapWrapper>
);
}
export interface OpenStreetMapComponentProps extends WidgetProps {
centerLat?: number;
centerLng?: number;
zoom?: number;
markerLat?: number;
markerLng?: number;
markerText?: string;
}
export default OpenStreetMapComponent;

View File

@ -0,0 +1,9 @@
// This file contains common constants which can be used across the widget configuration file (index.ts), widget and component folders.
export const OPENSTREETMAP_WIDGET_CONSTANT = "";
export enum OverflowTypes {
SCROLL = "SCROLL",
TRUNCATE = "TRUNCATE",
NONE = "NONE",
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="img"><title>OpenStreetMap icon</title><path d="M2.672 23.969c-.352-.089-.534-.234-1.471-1.168C.085 21.688.014 21.579.018 20.999c0-.645-.196-.414 3.368-3.986 3.6-3.608 3.415-3.451 4.064-3.449.302 0 .378.016.62.14l.277.14 1.744-1.744-.218-.343c-.425-.662-.825-1.629-1.006-2.429a7.657 7.657 0 0 1 1.479-6.44c2.49-3.12 6.959-3.812 10.26-1.588 1.812 1.218 2.99 3.099 3.328 5.314.07.467.07 1.579 0 2.074a7.554 7.554 0 0 1-2.205 4.402 6.712 6.712 0 0 1-1.943 1.401c-.959.483-1.775.71-2.881.803-1.573.131-3.32-.305-4.656-1.163l-.343-.218-1.744 1.744.14.28c.125.241.14.316.14.617.003.651.156.467-3.426 4.049-2.761 2.756-3.186 3.164-3.398 3.261-.271.125-.69.171-.945.106zM17.485 13.95a6.425 6.425 0 0 0 4.603-3.51c1.391-2.899.455-6.306-2.227-8.108-.638-.43-1.529-.794-2.367-.962-.581-.117-1.809-.104-2.414.025a6.593 6.593 0 0 0-2.452 1.064c-.444.315-1.177 1.048-1.487 1.487a6.384 6.384 0 0 0 .38 7.907 6.406 6.406 0 0 0 3.901 2.136c.509.078 1.542.058 2.065-.037zm-3.738 7.376a80.97 80.97 0 0 1-2.196-.651c-.025-.028 1.207-4.396 1.257-4.449.023-.026 4.242 1.152 4.414 1.236.062.026-.003.288-.525 2.102a398.513 398.513 0 0 0-.635 2.236c-.025.087-.069.156-.097.156-.028-.003-1.028-.287-2.219-.631zm2.912.524c0-.053 1.227-4.333 1.246-4.347.047-.034 4.324-1.23 4.341-1.211.019.019-1.199 4.337-1.23 4.36-.02.019-4.126 1.191-4.259 1.218-.054.011-.098 0-.098-.019zm-7.105-1.911c.846-.852 1.599-1.627 1.674-1.728.171-.218.405-.732.472-1.015.026-.118.053-.352.058-.522l.011-.307.182-.051c.103-.028.193-.044.202-.034.023.025-1.207 4.321-1.246 4.36-.02.016-.677.213-1.464.436l-1.425.405 1.537-1.542zm8.289-3.06a1.371 1.371 0 0 1-.059-.187l-.044-.156.156-.028c1.339-.227 2.776-.856 3.908-1.713.16-.125.252-.171.265-.134.054.165.272.95.265.959-.034.034-4.48 1.282-4.492 1.261zm-15.083-1.3c-.05-.039-1.179-3.866-1.264-4.29-.016-.084.146-.044 2.174.536 2.121.604 2.192.629 2.222.74.028.098.011.129-.125.223-.084.059-.769.724-1.523 1.479a63.877 63.877 0 0 1-1.39 1.367c-.016 0-.056-.025-.093-.054zm.821-4.378c-1.188-.343-2.164-.623-2.167-.626-.016-.012 1.261-4.433 1.285-4.46.022-.022 4.422 1.211 4.469 1.252.009.009-.269 1.017-.618 2.239-.576 2.02-.643 2.224-.723 2.22-.05-.003-1.059-.285-2.247-.626zm2.959.538c.012-.031.212-.723.444-1.534l.42-1.476.056.321c.093.556.265 1.188.464 1.741.106.296.187.539.181.545-.008.006-.332.101-.719.212-.389.109-.741.21-.786.224-.058.016-.075.006-.059-.034zM4.905 6.112c-1.187-.339-2.167-.635-2.18-.654-.04-.062-1.246-4.321-1.23-4.338.026-.025 4.31 1.204 4.351 1.246.047.051 1.28 4.379 1.246 4.376L4.91 6.113zm2.148-1.713l-.519-1.806-.078-.28 1.693-.483c.934-.265 1.724-.495 1.76-.508.034-.016-.083.14-.26.336A8.729 8.729 0 0 0 7.69 5.23a4.348 4.348 0 0 0-.132.561c0 .293-.115-.025-.505-1.39z"/></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,3 @@
import Widget from "./widget";
export default Widget;

View File

@ -0,0 +1,203 @@
import React from "react";
import type { DerivedPropertiesMap } from "WidgetProvider/factory/types";
import BaseWidget from "widgets/BaseWidget";
import type { WidgetProps, WidgetState } from "widgets/BaseWidget";
import OpenStreetMapComponent from "../component";
import { ValidationTypes } from "constants/WidgetValidation";
import IconSVG from "../icon.svg";
import { WIDGET_TAGS } from "constants/WidgetConstants";
class OpenStreetMapWidget extends BaseWidget<OpenStreetMapWidgetProps, WidgetState> {
static type = "OPENSTREETMAP_WIDGET";
//Метаданные
static getConfig() {
return {
name: "OpenStreetMap",
iconSVG: IconSVG,
needsMeta: false,
isCanvas: false,
tags: [WIDGET_TAGS.CONTENT],
searchTags: ["map", "openstreet", "open"],
};
}
static getFeatures() {
return {
dynamicHeight: {
sectionIndex: 0,
active: false,
},
};
}
//Значения по умолчанию
static getDefaults() {
return {
widgetName: "OpenStreetMap",
rows: 10,
columns: 10,
version: 1,
// Значения по умолчанию для карты
centerLat: 55.751244,
centerLng: 37.618423,
zoom: 7,
markerLat: 55.751244,
markerLng: 37.618423,
markerText: "Привет! Это Москва 🏙️",
};
}
static getPropertyPaneContentConfig() {
return [
{
sectionName: "General",
children: [
{
propertyName: "centerLat",
helpText: "Широта центра карты",
label: "Широта центра",
controlType: "INPUT_TEXT",
placeholderText: "55.751244",
defaultValue: 55.751244,
isBindProperty: true,
isTriggerProperty: false,
validation: {
type: ValidationTypes.NUMBER,
params: {
min: -90,
max: 90,
},
},
},
{
propertyName: "centerLng",
helpText: "Долгота центра карты",
label: "Долгота центра",
controlType: "INPUT_TEXT",
placeholderText: "37.618423",
defaultValue: 37.618423,
isBindProperty: true,
isTriggerProperty: false,
validation: {
type: ValidationTypes.NUMBER,
params: {
min: -180,
max: 180,
},
},
},
{
propertyName: "zoom",
helpText: "Уровень масштабирования карты (1-18)",
label: "Масштаб",
controlType: "INPUT_TEXT",
placeholderText: "7",
defaultValue: 7,
isBindProperty: true,
isTriggerProperty: false,
validation: {
type: ValidationTypes.NUMBER,
params: {
min: 1,
max: 18,
},
},
},
{
propertyName: "markerLat",
helpText: "Широта маркера",
label: "Широта маркера",
controlType: "INPUT_TEXT",
placeholderText: "55.751244",
defaultValue: 55.751244,
isBindProperty: true,
isTriggerProperty: false,
validation: {
type: ValidationTypes.NUMBER,
params: {
min: -90,
max: 90,
},
},
},
{
propertyName: "markerLng",
helpText: "Долгота маркера",
label: "Долгота маркера",
controlType: "INPUT_TEXT",
placeholderText: "37.618423",
defaultValue: 37.618423,
isBindProperty: true,
isTriggerProperty: false,
validation: {
type: ValidationTypes.NUMBER,
params: {
min: -180,
max: 180,
},
},
},
{
propertyName: "markerText",
helpText: "Текст во всплывающем окне маркера",
label: "Текст маркера",
controlType: "INPUT_TEXT",
placeholderText: "Привет! Это Москва 🏙️",
defaultValue: "Привет! Это Москва 🏙️",
isBindProperty: true,
isTriggerProperty: false,
validation: {
type: ValidationTypes.TEXT,
},
},
{
propertyName: "isVisible",
helpText: "Управляет видимостью виджета",
label: "Видимый",
controlType: "SWITCH",
isJSConvertible: true,
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.BOOLEAN },
},
],
},
];
}
static getPropertyPaneStyleConfig() {
return [];
}
static getDerivedPropertiesMap(): DerivedPropertiesMap {
return {};
}
static getDefaultPropertiesMap(): Record<string, string> {
return {};
}
static getMetaPropertiesMap(): Record<string, any> {
return {};
}
//Рендерит компонент
getWidgetView() {
return <OpenStreetMapComponent {...this.props} />;
}
}
export interface OpenStreetMapWidgetProps extends WidgetProps {
centerLat?: number;
centerLng?: number;
zoom?: number;
markerLat?: number;
markerLng?: number;
markerText?: string;
}
export default OpenStreetMapWidget;

View File

@ -368,6 +368,10 @@ const WidgetLoaders = new Map<string, () => Promise<typeof BaseWidget>>([
"EXTERNAL_WIDGET",
async () => import("./ExternalWidget").then((m) => m.default),
],
[
"OPENSTREETMAP_WIDGET",
async () => import("./OpenStreetMapWidget").then((m) => m.default),
],
// Deprecated Widgets
[

View File

@ -7436,6 +7436,17 @@ __metadata:
languageName: node
linkType: hard
"@react-leaflet/core@npm:^1.1.1":
version: 1.1.1
resolution: "@react-leaflet/core@npm:1.1.1"
peerDependencies:
leaflet: ^1.7.1
react: ^17.0.1
react-dom: ^17.0.1
checksum: 2fc4a80e5524f9437ac6cef0f95e63388f2df6ecc5107fef85fd097eb2455436e796d41a4c43cdcbb983a4132403646823ba1dc0d953e866eb80808cbfaf0232
languageName: node
linkType: hard
"@react-spectrum/utils@npm:^3.11.10, @react-spectrum/utils@npm:^3.9.0":
version: 3.11.10
resolution: "@react-spectrum/utils@npm:3.11.10"
@ -13837,6 +13848,7 @@ __metadata:
json5: ^2.2.3
klona: ^2.0.5
knip: ^5.30.2
leaflet: ^1.9.4
libphonenumber-js: ^1.9.44
linkedom: ^0.14.20
lint-staged: ^14.0.1
@ -13900,6 +13912,7 @@ __metadata:
react-hook-form: ^7.28.0
react-is: ^16.12.0
react-json-view: ^1.21.3
react-leaflet: 3.2.5
react-media-recorder: ^1.6.1
react-modal: ^3.15.1
react-page-visibility: ^7.0.0
@ -24138,6 +24151,13 @@ __metadata:
languageName: node
linkType: hard
"leaflet@npm:^1.9.4":
version: 1.9.4
resolution: "leaflet@npm:1.9.4"
checksum: bfc79f17a247b37b92d84b3c78702501603392d6589fde606de4a825d11f1609d90225388834f2e0709dac327e52dcd4b4b9cc9fd3d590060c5b1e53b84fa6c6
languageName: node
linkType: hard
"leven@npm:^3.1.0":
version: 3.1.0
resolution: "leven@npm:3.1.0"
@ -29956,6 +29976,19 @@ __metadata:
languageName: node
linkType: hard
"react-leaflet@npm:3.2.5":
version: 3.2.5
resolution: "react-leaflet@npm:3.2.5"
dependencies:
"@react-leaflet/core": ^1.1.1
peerDependencies:
leaflet: ^1.7.1
react: ^17.0.1
react-dom: ^17.0.1
checksum: 503b7cee8acc12e0e2c5e7675432e7ef5742463e3e5420282ce60a9efd306430caefae3cb282c976e3df7665f19aef5b49cdce44a034979b4ea3ee968c2621d2
languageName: node
linkType: hard
"react-lifecycles-compat@npm:^3.0.0, react-lifecycles-compat@npm:^3.0.4":
version: 3.0.4
resolution: "react-lifecycles-compat@npm:3.0.4"