function path(canvas, pathData) {
	/** @type {PathElement} */
	const svgGrp = svgEl('g', `
		<path data-key="outer" d="M0 0" stroke="transparent" stroke-width="20" fill="none" />
		<path data-key="path" class="path" d="M0 0" stroke="#495057" stroke-width="1.8" fill="none" style="pointer-events: none;" />
		<path data-key="selected" d="M0 0" stroke="transparent" stroke-width="10" fill="none" style="pointer-events: none;" />
		<g data-key="start">
			<circle data-evt-index="1" class="path-end" r="10" stroke-width="0" fill="transparent" />
			<path class="path" d="M-7 7 l 7 -7 l -7 -7" stroke="#495057" stroke-width="1.8" fill="none" style="pointer-events: none;"></path>
		</g>
		<g data-key="end">
			<circle data-evt-index="1" class="path-end" r="10" stroke-width="0" fill="transparent" />
			<path class="path" d="M-7 7 l 7 -7 l -7 -7" stroke="#495057" stroke-width="1.8" fill="none" style="pointer-events: none;"></path>
		</g>`);
	classAdd(svgGrp, 'shpath');

	pathData.s.el = child(svgGrp, 'start');
	pathData.e.el = child(svgGrp, 'end');
	pathData.styles = pathData.styles ?? ['arw-e'];
	const paths = childs(svgGrp, 'path', 'outer', 'selected');

	function draw() {
		if (!pathData.s.shape || !pathData.e.shape) {
			const endDir = dirByAngle(pathData.s.data.position, pathData.e.data.position);
			if (!pathData.e.shape) { pathData.e.data.dir = endDir; }
			if (!pathData.s.shape) { pathData.s.data.dir = dirReverse(endDir); }
		}

		// path
		const dAttr = pathCalc(pathData);
		paths.forEach(pp => pp.setAttribute('d', dAttr));

		// ends
		endDraw(pathData.s);
		endDraw(pathData.e);
	}

	/** @param {PathEnd} pathEnd */
	function pathDelFromShape(pathEnd) { shapeObj(pathEnd.shape)?.pathDel(svgGrp); }

	/** @param {PathEnd} pathEnd */
	function pathAddToShape(pathEnd) {
		if (pathEnd.shape) {
			pathEnd.data = shapeObj(pathEnd.shape).pathAdd(pathEnd.shape.connectorKey, svgGrp);
		}
	};

	/** @type { {position:(bottomX:number, bottomY:number)=>void, del:()=>void} } */
	let settingsPnl;
	function del() {
		unSelect();
		reset();
		pathDelFromShape(pathData.s);
		pathDelFromShape(pathData.e);
		svgGrp.remove();
	}

	/**
	 * @type {0|1|2}
	 * 0 - init, 1 - selected, 2 - edit
	*/
	let state = 0;

	/** @type {()=>void} */
	let listenCopyDispose;

	/** @param {PointerEvent} evt */
	function select(evt) {
		// in edit mode
		if (state === 2) { return; }

		// to edit mode
		if (state === 1) {
			state = 2;
			settingsPnl = modalCreate(evt.clientX - 10, evt.clientY - 10, new PathSettings(canvas, svgGrp));
			return;
		}

		// to select mode
		state = 1;
		classAdd(svgGrp, 'select');
		endSetEvtIndex(pathData.s, 2);
		endSetEvtIndex(pathData.e, 2);

		canvasSelectionClearSet(canvas, unSelect);
		listenCopyDispose = listenCopy(() => [svgGrp]);
	};

	/** @type { {():void} } */
	let hoverEmulateDispose;
	function unSelect() {
		state = 0;
		classDel(svgGrp, 'select');
		endSetEvtIndex(pathData.s, 1);
		endSetEvtIndex(pathData.e, 1);

		settingsPnl?.del();	settingsPnl = null;

		if (hoverEmulateDispose) {
			hoverEmulateDispose();
			hoverEmulateDispose = null;
			svgGrp.style.pointerEvents = 'unset';
		}

		canvasSelectionClearSet(canvas, null);
		if (listenCopyDispose) { listenCopyDispose(); listenCopyDispose = null;	}
	};

	/** @type {'s'|'e'} */
	let movedEnd;

	const reset = moveEvtProc(
		canvas.ownerSVGElement,
		svgGrp,
		canvas[CanvasSmbl].data,
		// data.end.position,
		{
			get x() { return pathData[movedEnd]?.data.position.x; },
			set x(val) { if (movedEnd) { pathData[movedEnd].data.position.x = val; } },

			get y() { return pathData[movedEnd]?.data.position.y; },
			set y(val) { if (movedEnd) { pathData[movedEnd].data.position.y = val; } }
		},
		// onMoveStart
		/** @param {PointerEvent & { target: Element} } evt */ evt => {
			unSelect();

			movedEnd = pathData.e.el.contains(evt.target) ? 'e' : pathData.s.el.contains(evt.target) ? 's' : null;

			//
			// move whole path
			if (!movedEnd) {
				return;
			}

			//
			// move path end

			// disconnect from shape
			if (pathData[movedEnd].shape) {
				if (pathData[movedEnd].shape.shapeEl !== pathData[movedEnd === 's' ? 'e' : 's'].shape?.shapeEl) {
					pathDelFromShape(pathData[movedEnd]);
				}
				pathData[movedEnd].shape = null;
				pathData[movedEnd].data = {
					dir: pathData[movedEnd].data.dir,
					position: pointInCanvas(canvas[CanvasSmbl].data, evt.clientX, evt.clientY)
				};
			}

			// hover emulation - start
			svgGrp.style.pointerEvents = 'none';
			hoverEmulateDispose = hoverEmulate(svgGrp.parentElement);
		},
		// onMove
		/** @param {PointerEventFixMovement} evt */
		evt => {
			if (!movedEnd) {
				moveWholePath(canvas[CanvasSmbl].data, pathData, draw, evt);
			} else {
				draw();
			}
		},
		// onMoveEnd
		evt => {
			if (!movedEnd) {
				moveWholePathFinish(canvas[CanvasSmbl].data, pathData, draw);
			} else {
				// connect to shape
				const elemFromPoint = priorityElemFromPoint(evt);
				const connectorKey = elemFromPoint?.getAttribute('data-connect');
				if (connectorKey) {
					// @ts-ignore
					pathData[movedEnd].shape = { shapeEl: elemFromPoint.parentElement, connectorKey };
					pathAddToShape(pathData[movedEnd]);
				} else {
					placeToCell(pathData[movedEnd].data.position, canvas[CanvasSmbl].data.cell);
				}
				draw();
			}

			// hover emulation - end
			unSelect();
		},
		// onClick
		select,
		// onOutdown
		unSelect
	);

	svgGrp[PathSmbl] = {
		draw,
		/** @param {PointerEventInit} evt */
		pointerCapture: evt => pathData.e.el.dispatchEvent(new PointerEvent('pointerdown', evt)),
		del,
		data: pathData
	};

	if (pathData.styles) { classAdd(svgGrp, ...pathData.styles); }
	pathAddToShape(pathData.s);
	pathAddToShape(pathData.e);
	draw();

	return svgGrp;
}
@param {CanvasElement} canvas
@param {PathData} pathData