import React, { CSSProperties } from 'react'; import { useNode, UserComponent } from '@craftjs/core'; import { cssPropsToString } from '../../utils/style-helpers'; import { escapeAttr, safeUrl } from '../../utils/escape'; interface MapEmbedProps { address?: string; zoom?: number; height?: string; style?: CSSProperties; } function buildMapUrl(address: string, zoom: number): string { const encoded = encodeURIComponent(address); // `zoom` is declared as `number` but is not runtime-type-checked (AI // update_props / deserialized state can hand us anything). The final src // string is still run through escapeAttr(safeUrl(...)) at the toHtml call // site, which already blocks attribute-breakout -- but Number-coercing // here too keeps the emitted URL a well-formed `z=` query param // instead of smuggling arbitrary attacker text into it. const z = Number(zoom); const safeZoom = Number.isFinite(z) ? z : 14; return `https://maps.google.com/maps?q=${encoded}&z=${safeZoom}&output=embed`; } export const MapEmbed: UserComponent = ({ address = 'New York, NY', zoom = 14, height = '400px', style = {}, }) => { const { connectors: { connect, drag }, selected, } = useNode((node) => ({ selected: node.events.selected, })); return (
{ if (ref) connect(drag(ref)); }} style={{ width: '100%', outline: selected ? '2px solid #3b82f6' : 'none', ...style, }} >
`, }; };