feat(gps): Яндекс-подложка переведена на JS API 2.1 (v3 без бесплатного тарифа), общий marker-html для обеих подложек
This commit is contained in:
46
client/src/components/gps/marker-html.ts
Normal file
46
client/src/components/gps/marker-html.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { GpsMapMarker } from './GpsMap';
|
||||
|
||||
/**
|
||||
* Общая HTML-разметка кастомных маркеров GPS-модуля.
|
||||
* Используется обеими реализациями карты: Leaflet (divIcon) и Яндекс.Карты 2.1
|
||||
* (templateLayoutFactory) — визуально маркеры одинаковые на обеих подложках.
|
||||
*/
|
||||
|
||||
const DEFAULT_COLOR = '#3b82f6';
|
||||
|
||||
// Иконки lucide (car / user) в виде SVG-строк: react-компоненты внутрь
|
||||
// HTML-маркера карты вставить нельзя, разметка собирается вручную
|
||||
const CAR_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 17h2c.6 0 1-.4 1-1v-3c0-.9-.7-1.7-1.5-1.9C18.7 10.6 16 10 16 10s-1.3-1.4-2.2-2.3c-.5-.4-1.1-.7-1.8-.7H5c-.6 0-1.1.4-1.4.9l-1.4 2.9A3.7 3.7 0 0 0 2 12v4c0 .6.4 1 1 1h2"/><circle cx="7" cy="17" r="2"/><path d="M9 17h6"/><circle cx="17" cy="17" r="2"/></svg>`;
|
||||
const USER_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>`;
|
||||
|
||||
/** Экранирование пользовательского текста для HTML-разметки маркера */
|
||||
function escapeHtml(text: string): string {
|
||||
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML маркера: цветной кружок с иконкой (vehicle/person) или маленькая точка
|
||||
* (dot) + подпись снизу; offline — приглушённый (opacity 0.5).
|
||||
* Внутренний контейнер центрируется через translate(-50%, -50%) — точка
|
||||
* привязки карты должна быть в левом верхнем углу элемента.
|
||||
*/
|
||||
export function gpsMarkerHtml(marker: GpsMapMarker): string {
|
||||
const color = marker.color || DEFAULT_COLOR;
|
||||
const opacity = marker.offline ? 'opacity:0.5;' : '';
|
||||
const label = marker.label
|
||||
? `<div style="margin-top:2px;padding:0 4px;border-radius:4px;font-size:10px;line-height:16px;font-weight:500;background:rgba(255,255,255,0.9);border:1px solid #d4d4d8;white-space:nowrap;max-width:120px;overflow:hidden;text-overflow:ellipsis;color:#18181b;">${escapeHtml(marker.label)}</div>`
|
||||
: '';
|
||||
|
||||
if (marker.kind === 'dot') {
|
||||
return `<div style="display:flex;flex-direction:column;align-items:center;transform:translate(-50%,-50%);user-select:none;${opacity}">
|
||||
<div style="width:16px;height:16px;border-radius:50%;border:2px solid #fff;background:${color};box-shadow:0 1px 3px rgba(0,0,0,0.3);" title="${escapeHtml(marker.label || '')}"></div>
|
||||
${label}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
const icon = marker.kind === 'person' ? USER_SVG : CAR_SVG;
|
||||
return `<div style="display:flex;flex-direction:column;align-items:center;transform:translate(-50%,-50%);user-select:none;cursor:pointer;${opacity}">
|
||||
<div style="display:flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;border:2px solid #fff;background:${color};box-shadow:0 1px 4px rgba(0,0,0,0.4);">${icon}</div>
|
||||
${label}
|
||||
</div>`;
|
||||
}
|
||||
Reference in New Issue
Block a user