import { _objectSpread2 } from "../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
import MapLayer from "./components/MapLayer.js";
import ItemLayer from "./components/ItemLayer.js";
import EditableItemLayer from "./components/EditableItemLayer.js";
import { FLOOR_MAP_RESOURCE_PLACE_DEFAULT, getElementKindConfig, getSceneElementRenderIssue } from "./utils/floorMapElementKindUtils.js";
import { isFloorMapPaletteDrag, parsePaletteDragPayload, sceneXYFromContentDropOffset } from "./utils/floorMapDropUtils.js";
import { FLOOR_MAP_LAYOUT_CLASS_PREFIX, FLOOR_MAP_OVERVIEW_TAB_ID } from "./floorMapLayoutConstants.js";
import FloorMapToolbar from "./components/FloorMapToolbar.js";
import useEngineContext from "../../hooks/useEngineContext.js";
import { getFloorMapDisplayLocale, initFloorMapLayoutLocales } from "./utils/floorMapCanvasDisplay.js";
import CanvasTabs from "./components/CanvasTabs.js";
import FloorMapEditTopBar from "./components/FloorMapEditTopBar.js";
import { mergeCanvasesForTabs } from "./utils/mergeCanvasesForTabs.js";
import FloorMapOverview from "./components/FloorMapOverview.js";
import FloorMapEditPanel from "./components/FloorMapEditPanel.js";
import { gapToPx } from "./utils/layoutMeasurements.js";
import { FloorMapEdgeEditPanel } from "./components/FloorMapEdgeEditPanel.js";
import { EdgeLayer } from "./components/EdgeLayer.js";
import { NodePortMarkers } from "./components/NodePortMarkers.js";
import { useFloorMapEdgeViewModel } from "./hooks/useFloorMapEdgeViewModel.js";
import { useFloorMapEdgeEditing } from "./hooks/useFloorMapEdgeEditing.js";
import { ViewControlsWithZoom } from "./components/ViewControlsWithZoom.js";
import { useFloorMapEditState } from "./hooks/useFloorMapEditState.js";
import { useFloorMapTransformMetrics } from "./hooks/useFloorMapTransformMetrics.js";
import { renderFloorMapFallbackPlaceholder, renderFloorMapRenderError } from "./utils/floorMapFallbackRender.js";
import { getFloorMapAppearanceTokens } from "./appearance/floorMapAppearance.js";
import { useFloorMapPaletteTouchDrag } from "./hooks/useFloorMapPaletteTouchDrag.js";
import { buildFloorMapOverviewLayout } from "./utils/floorMapOverviewLayout.js";
import { useFloorMapLayoutContext } from "./context/FloorMapLayoutContext.js";
import { mergeFloorMapLayoutPropsFromContext } from "./context/mergeFloorMapLayoutContext.js";
import React, { useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
import { locales } from "@pisell/utils";
import { Drawer, theme } from "antd";
import classNames from "classnames";
import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
import "./PisellFloorMapLayout.less";
//#region src/components/pisellFloorMapLayout/PisellFloorMapLayout.tsx
/**
* PisellFloorMapLayout - 空间布局视图
* 支持两种用法：1) 仅传入 items + renderItem（原有用法）；2) 传入 floorMapConfig + dataSources + mode 使用平面图视图（含多画布/编辑态）
*/
/**
* 布局根实现（由 {@link PisellFloorMapLayout} forwardRef 包裹）。
* 负责：上下文与 props 合并、编辑态/阅读态分支、Transform 视口、图元层与工具栏联动。
*/
function PisellFloorMapLayoutInner(props, ref) {
	var _ref, _currentCanvas$mapLay2, _currentCanvas$mapLay3, _mapLayer$backgroundC, _mapLayer$boundingPad, _config$edges, _config$edges2, _config$sceneElements;
	const { className, style, items: itemsProp, renderItem, rowKey, renderItemByKind, getMergedItemWrapperStyle, enableEdges = false, mapLayer, cellSize: cellSizeProp, itemUnit = "cell", zoom = {}, pan = {}, controls = {}, enableHover = true, readModeSelectedItemId, onItemClick, onItemHover, onItemLeave, mode = "read", floorMapConfig: floorMapConfigProp, dataSources, onSave, onExitEdit, onDataSourceRecordSave, dataSourceLabels, dataSourceFormColumns, dataSourceRecordForm, loading, saveError, onElementRenderError, toolbarShowSave = true, renderResourcePickerModal, renderPaletteViewAllModal, paletteSidebarVisibleLimit, floorMapFullscreenMode, appearanceMode = "light", showDataSourceRecordFormBlock, floorMapViewportOverlay, viewportOverlayPlacement = "top-start", viewportOverlayClassName, editTopBarPhoneLayout = false, canvasTabsBarTrailing, viewportDockToolButtons, viewportDockTrailing, viewportPersist = true, viewportPersistNamespace } = mergeFloorMapLayoutPropsFromContext(props, useFloorMapLayoutContext());
	const { token } = theme.useToken();
	const isFloorMapView = Boolean(floorMapConfigProp);
	const contentOffsetRef = useRef({
		x: 0,
		y: 0
	});
	const mapDropLayerRef = useRef(null);
	const transformRef = useRef(null);
	const focusViewportOnBoundsRef = useRef(null);
	const { config, currentCanvasId, setCurrentCanvasId, selectedId, setSelectedId, placeMode, setPlaceMode, items, saving, savingRecord, handleAddCanvas, handleRemoveCanvas, handleRenameCanvas, handleUpdateItem, handleUpdateItemsBatch, handleCanvasPlaceClick, handleSave, placeSceneElementFromPaletteDrop, bulkPlacePaletteItems, editPanelParams, placeOverlayClass, isLayoutDirty, toggleDraftCanvasShowGrid, toggleSelectedLayoutLock, duplicateSelectedElement, deleteSelectedElement, undoDraft, redoDraft, canUndo, canRedo, discardDraft, commitDraft } = useFloorMapEditState({
		floorMapConfigProp,
		isFloorMapView,
		itemsProp,
		cellSizeProp,
		dataSources,
		onSave,
		onDataSourceRecordSave,
		dataSourceFormColumns,
		dataSourceRecordForm,
		dataSourceLabels,
		layoutPrefix: FLOOR_MAP_LAYOUT_CLASS_PREFIX,
		contentOffsetRef,
		showDataSourceRecordFormBlock,
		focusViewportOnBoundsRef
	});
	const mapContentRef = useRef(null);
	/**
	* 当前选中的画布配置：用于提取当前画布级 mapLayer 显式配置。
	*/
	const currentCanvas = useMemo(() => {
		var _config$canvases$find;
		if (!isFloorMapView || !config || !currentCanvasId) return null;
		return (_config$canvases$find = config.canvases.find((canvas) => canvas.id === currentCanvasId)) !== null && _config$canvases$find !== void 0 ? _config$canvases$find : null;
	}, [
		isFloorMapView,
		config,
		currentCanvasId
	]);
	/**
	* 「取消编辑」组合：先丢弃 draft（同步把 draftConfig replace 回上游 props 的值），
	* 再调用业务侧 onExitEdit 退出编辑模式。
	*
	* 不在 onExitEdit 本身做 discard，是因为「完成」按钮链路也会调 onExitEdit，
	* 完成时草稿应保留等 props 回写（参考 useFloorMapEditState.handleSave 的 baseline 处理），
	* 一旦在 onExitEdit 里 discard 会把保存中的视觉态闪回旧值。
	*/
	const handleCancelEdit = useCallback(() => {
		discardDraft();
		onExitEdit === null || onExitEdit === void 0 || onExitEdit();
	}, [discardDraft, onExitEdit]);
	/** 当前画布下的 mapLayer：全局 `mapLayer` 与画布级配置合并，供 MapLayer / 吸附等读取 */
	const effectiveMapLayer = useMemo(() => {
		var _currentCanvas$mapLay;
		if (!isFloorMapView || !currentCanvas) return mapLayer;
		return _objectSpread2(_objectSpread2({}, mapLayer !== null && mapLayer !== void 0 ? mapLayer : {}), (_currentCanvas$mapLay = currentCanvas.mapLayer) !== null && _currentCanvas$mapLay !== void 0 ? _currentCanvas$mapLay : {});
	}, [
		isFloorMapView,
		currentCanvas,
		mapLayer
	]);
	const appearanceTokens = useMemo(() => getFloorMapAppearanceTokens(appearanceMode), [appearanceMode]);
	const canvasBackgroundColor = (_ref = (_currentCanvas$mapLay2 = currentCanvas === null || currentCanvas === void 0 || (_currentCanvas$mapLay3 = currentCanvas.mapLayer) === null || _currentCanvas$mapLay3 === void 0 ? void 0 : _currentCanvas$mapLay3.backgroundColor) !== null && _currentCanvas$mapLay2 !== void 0 ? _currentCanvas$mapLay2 : mapLayer === null || mapLayer === void 0 ? void 0 : mapLayer.backgroundColor) !== null && _ref !== void 0 ? _ref : appearanceTokens.fallbackCanvasBackgroundColor;
	/**
	* All 视图整体背景色：不再按各画布区域分别取色（用户明确要求「不要按区域来」），
	* 改成跟单画布同一套**根级 + appearance 兜底**链。这样：
	* - root `mapLayer.backgroundColor` 配的深色背景，All 视图整张背景都吃到；
	* - 没显式配色时按 `appearanceMode`（dark/light）兜底；
	* - 画布级的 `canvas.mapLayer.backgroundColor` 在 All 视图刻意不读，避免某张画布
	*   的私有背景污染整体 All 视图。
	*/
	const overviewBackgroundColor = (_mapLayer$backgroundC = mapLayer === null || mapLayer === void 0 ? void 0 : mapLayer.backgroundColor) !== null && _mapLayer$backgroundC !== void 0 ? _mapLayer$backgroundC : appearanceTokens.fallbackCanvasBackgroundColor;
	/**
	* 必须与下方 TransformWrapper 的 `key` 完全一致。
	* 远程/合并配置落地后 boundingPadding 等会变 → Wrapper 重挂载 → 若 autoFit 仍按旧「仅 canvasId」帧认为已 done，首屏会停在初始 transform；切换画布才因 id 变化再次 fit。
	*/
	/**
	* 「全览」状态：仅阅读态下、启用画布 ≥ 2 时通过画布 Tab 最前的「All」入口进入。
	* 不写入 `currentCanvasId`，避免：
	*   - useFloorMapEditState 内的「画布存在性同步」effect 把当前 id 立刻 reset 掉；
	*   - 触发 transformWrapperKey 变化导致单画布视角缓存被清。
	* 仅由本组件根据 mode/画布数量切换；外部不感知。
	*/
	const [isOverview, setIsOverview] = useState(false);
	/**
	* 与下方 TransformWrapper `key` 完全一致；远程合并 padding/默认缩放回包后变化触发重挂载，
	* 单画布 ↔ 全览（All）切换也通过本 key 切换，避免视角与缓存交叉污染。
	*/
	const transformWrapperKey = useMemo(() => {
		var _ref2, _zoom$defaultScale;
		if (!isFloorMapView) return "fmap-layout";
		const bp = (_ref2 = isOverview ? mapLayer === null || mapLayer === void 0 ? void 0 : mapLayer.boundingPadding : effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.boundingPadding) !== null && _ref2 !== void 0 ? _ref2 : "d";
		const z = (_zoom$defaultScale = zoom.defaultScale) !== null && _zoom$defaultScale !== void 0 ? _zoom$defaultScale : 1;
		if (isOverview) return `fmap-overview-bp${bp}-z${z}`;
		return `fmap-${currentCanvasId !== null && currentCanvasId !== void 0 ? currentCanvasId : "na"}-bp${bp}-z${z}`;
	}, [
		isFloorMapView,
		isOverview,
		currentCanvasId,
		effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.boundingPadding,
		mapLayer === null || mapLayer === void 0 ? void 0 : mapLayer.boundingPadding,
		zoom.defaultScale
	]);
	/** 供顶栏 Tab 使用：合并草稿画布等后的画布列表（基础列表，不含「全览」） */
	const canvasesForTabsBase = useMemo(() => config ? mergeCanvasesForTabs(config) : [], [config]);
	const engineLocale = getFloorMapDisplayLocale(useEngineContext());
	const hasCanvasTabsBarTrailing = canvasTabsBarTrailing != null && canvasTabsBarTrailing !== false;
	const enabledCanvasCount = useMemo(() => canvasesForTabsBase.filter((c) => c.enabled).length, [canvasesForTabsBase]);
	/** 仅阅读态、启用画布 > 1 时，在 Tab 列表末尾插入「Overview」伪 tab */
	const showOverviewTab = isFloorMapView && mode === "read" && enabledCanvasCount > 1;
	const overviewTabLabel = useMemo(() => {
		initFloorMapLayoutLocales(engineLocale);
		return locales.getText("pisell-floor-map-layout.canvas-tabs.overview");
	}, [engineLocale]);
	/**
	* 给 CanvasTabs 渲染用的最终列表：阅读态多画布时在列表末尾插入「Overview」伪 tab。
	* order 取当前最大 order + 1，保证排序后位于最后一个。
	*/
	const canvasesForTabs = useMemo(() => {
		if (!showOverviewTab) return canvasesForTabsBase;
		const maxOrder = canvasesForTabsBase.reduce((max, c) => c.order > max ? c.order : max, Number.NEGATIVE_INFINITY);
		const overviewTab = {
			id: FLOOR_MAP_OVERVIEW_TAB_ID,
			name: overviewTabLabel,
			order: Number.isFinite(maxOrder) ? maxOrder + 1 : 0,
			enabled: true
		};
		return [...canvasesForTabsBase, overviewTab];
	}, [
		canvasesForTabsBase,
		showOverviewTab,
		overviewTabLabel
	]);
	/** 编辑态强制退出全览（多画布编辑流程不应进入只读全览界面） */
	useEffect(() => {
		if (mode !== "read") {
			if (isOverview) setIsOverview(false);
			return;
		}
		/** 阅读态下若启用画布回落到 1 张以内，全览 Tab 已不再展示，需自动退出 */
		if (isOverview && enabledCanvasCount <= 1) setIsOverview(false);
	}, [
		mode,
		isOverview,
		enabledCanvasCount
	]);
	/** 阅读态是否展示画布 Tab：多启用画布或配置了 `canvasTabsBarTrailing` 时出现 */
	const showReadModeCanvasTabsBar = useMemo(() => {
		if (!isFloorMapView || mode !== "read" || !config) return false;
		if (hasCanvasTabsBarTrailing) return true;
		return enabledCanvasCount > 1;
	}, [
		isFloorMapView,
		mode,
		config,
		hasCanvasTabsBarTrailing,
		enabledCanvasCount
	]);
	/** 阅读态画布 Tab 工具条的无障碍标签（随引擎语言初始化文案） */
	const readCanvasTabsAriaLabel = useMemo(() => {
		initFloorMapLayoutLocales(engineLocale);
		return locales.getText("pisell-floor-map-layout.read-canvas-tabs.aria");
	}, [engineLocale]);
	/** 加载遮罩上的文案 */
	const loadingLabel = useMemo(() => {
		initFloorMapLayoutLocales(engineLocale);
		return locales.getText("pisell-floor-map-layout.loading");
	}, [engineLocale]);
	/** 选中图元旁：锁定（与侧栏同字段）/ 复制 / 删除；锁定时不展开右侧属性面板 */
	const editQuickBar = useMemo(() => {
		if (!isFloorMapView || mode !== "edit" || !(config === null || config === void 0 ? void 0 : config.canvasUi.canEdit) || !selectedId) return null;
		initFloorMapLayoutLocales(engineLocale);
		const row = items.find((i) => i.id === selectedId);
		if (!(row === null || row === void 0 ? void 0 : row._sceneElement)) return null;
		return {
			labels: {
				lock: locales.getText("pisell-floor-map-layout.edit-quick.lock"),
				unlock: locales.getText("pisell-floor-map-layout.edit-quick.unlock"),
				duplicate: locales.getText("pisell-floor-map-layout.edit-quick.duplicate"),
				delete: locales.getText("pisell-floor-map-layout.edit-quick.delete"),
				popTitle: locales.getText("pisell-floor-map-layout.edit-panel.pop-title"),
				popDesc: locales.getText("pisell-floor-map-layout.edit-panel.pop-desc"),
				cancel: locales.getText("pisell-floor-map-layout.edit-panel.btn-cancel"),
				okDelete: locales.getText("pisell-floor-map-layout.edit-panel.btn-ok-delete"),
				ariaToolbar: locales.getText("pisell-floor-map-layout.edit-quick.aria-toolbar")
			},
			isLocked: Boolean(row._sceneElement.layoutTransformLocked),
			onToggleLock: toggleSelectedLayoutLock,
			onDuplicate: duplicateSelectedElement,
			onDelete: deleteSelectedElement
		};
	}, [
		isFloorMapView,
		mode,
		config === null || config === void 0 ? void 0 : config.canvasUi.canEdit,
		selectedId,
		items,
		engineLocale,
		toggleSelectedLayoutLock,
		duplicateSelectedElement,
		deleteSelectedElement
	]);
	const cellSize = cellSizeProp !== null && cellSizeProp !== void 0 ? cellSizeProp : 64;
	/** 图元间距（像素）：由 mapLayer.gap 解析 */
	const itemGap = useMemo(() => gapToPx(effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.gap), [effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.gap]);
	/** 平面图坐标始终按像素；非平面图沿用 props `itemUnit` */
	const effectiveItemUnit = isFloorMapView ? "pixel" : itemUnit;
	/**
	* 「全览」视图布局：把所有启用画布按各自原始尺寸（同一比例尺）紧凑装箱，
	* 主 TransformWrapper 用整体 totalWidth/Height 作为 stage 尺寸，
	* FloorMapOverview 按 areas[].x/y 把对应画布的图元绝对定位渲染。
	* 与单画布共用同一套缩放/平移/锁定/全屏/fitBounds。
	*/
	const overviewLayout = useMemo(() => {
		if (!isFloorMapView || !isOverview || !config) return null;
		return buildFloorMapOverviewLayout({
			config,
			globalMapLayer: mapLayer,
			cellSize,
			itemUnit: effectiveItemUnit,
			areaGap: 40
		});
	}, [
		isFloorMapView,
		isOverview,
		config,
		mapLayer,
		cellSize,
		effectiveItemUnit
	]);
	/**
	* 视口持久化 key：
	*   - 仅平面图模式启用（其它模式没有「画布」概念）
	*   - `viewportPersist === false` 或无 currentCanvasId 时关闭
	*   - 有 namespace 时拼成 `${ns}:${canvasId}`，隔离不同业务场景（店铺 / 平面图）
	*   - 未传 namespace 时 fallback 为仅 `${canvasId}`，小心跨场景可能共享视角
	*   - 不感知 boundingPadding / defaultScale 等渲染参数变化，避免 padding 调整导致缓存失效
	*   - 「全览」模式使用独立的 `__overview__` key，与单画布缓存互不影响
	*/
	const viewportPersistKey = useMemo(() => {
		if (viewportPersist === false) return void 0;
		if (!isFloorMapView) return void 0;
		if (isOverview) return viewportPersistNamespace ? `${viewportPersistNamespace}:__overview__` : "__overview__";
		if (!currentCanvasId) return void 0;
		return viewportPersistNamespace ? `${viewportPersistNamespace}:${currentCanvasId}` : currentCanvasId;
	}, [
		viewportPersist,
		viewportPersistNamespace,
		isFloorMapView,
		isOverview,
		currentCanvasId
	]);
	/**
	* 全览模式下喂给 metrics 的虚拟 item：让 stage / fitBounds 把整体当作一张
	* 0,0,totalWidth,totalHeight 的大图元处理；不参与渲染（渲染走 FloorMapOverview）。
	* 类型上仅需 FloorMapItemBase，metrics 内部不读 `_sceneElement` 等扩展字段。
	*/
	const overviewMetricsItems = useMemo(() => {
		if (!overviewLayout) return [];
		return [{
			id: "__pisell_floor_map_overview_stage__",
			x: 0,
			y: 0,
			width: overviewLayout.totalWidth,
			height: overviewLayout.totalHeight,
			shape: "rect"
		}];
	}, [overviewLayout]);
	const { containerRef, mapAreaRef, mapAreaSize, contentWidth, contentHeight, contentOffsetX, contentOffsetY, initialPositionX, initialPositionY, zoomConfig, scale, setScale, isFullscreen, toggleFullscreen, fitBounds, focusContentBounds, isAtFitBounds, notifyTransformed, persistCurrentViewport, attachTransformContext, isInteractingWithElement, setIsInteractingWithElement, viewportLocked, toggleViewportLocked } = useFloorMapTransformMetrics({
		items: isOverview && overviewLayout ? overviewMetricsItems : items,
		cellSize,
		itemGap: isOverview ? 0 : itemGap,
		effectiveItemUnit,
		isFloorMapView,
		mode,
		zoom,
		transformRef,
		contentOffsetRef,
		boundingPadding: isOverview ? (_mapLayer$boundingPad = mapLayer === null || mapLayer === void 0 ? void 0 : mapLayer.boundingPadding) !== null && _mapLayer$boundingPad !== void 0 ? _mapLayer$boundingPad : 56 : effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.boundingPadding,
		frameResetKey: isFloorMapView ? transformWrapperKey : void 0,
		viewportPersistKey
	});
	useEffect(() => {
		focusViewportOnBoundsRef.current = (bounds) => {
			requestAnimationFrame(() => {
				requestAnimationFrame(() => {
					focusContentBounds(bounds);
				});
			});
		};
		return () => {
			focusViewportOnBoundsRef.current = null;
		};
	}, [focusContentBounds]);
	/**
	* 是否启用连线（edge）渲染与编辑能力。
	* - 必须显式 `enableEdges={true}` 才打开；
	* - pisell-reservation 等通用业务画布默认关闭，避免选中图元出现锚点圆点或拖出连线。
	*/
	const showEdgeLayer = enableEdges && isFloorMapView && !isOverview && config != null;
	/** 连线层显隐（仅影响 EdgeLayer 绘制；编辑锚点仍随 `showEdgeLayer`） */
	const [edgesLayerVisible, setEdgesLayerVisible] = useState(true);
	const showEdgesOnCanvas = showEdgeLayer && edgesLayerVisible;
	const toggleEdgesLayerVisible = useCallback(() => {
		setEdgesLayerVisible((v) => !v);
	}, []);
	const readEdgeVm = useFloorMapEdgeViewModel({
		edges: showEdgeLayer && mode === "read" ? (_config$edges = config === null || config === void 0 ? void 0 : config.edges) !== null && _config$edges !== void 0 ? _config$edges : [] : [],
		items: showEdgeLayer ? items : [],
		cellSize,
		itemUnit: effectiveItemUnit,
		itemGap
	});
	const edgeEdit = useFloorMapEdgeEditing({
		enabled: showEdgeLayer && mode === "edit",
		config,
		items,
		mergedItems: items,
		cellSize,
		itemUnit: effectiveItemUnit,
		itemGap,
		contentOffsetRef,
		selectedId,
		setSelectedId,
		commitDraft,
		mapContentRef,
		scale
	});
	const edgeRoutes = mode === "edit" && showEdgeLayer ? edgeEdit.routes : readEdgeVm.routes;
	const edgeRectsById = mode === "edit" && showEdgeLayer ? edgeEdit.rectsById : readEdgeVm.rectsById;
	const displayEdges = mode === "edit" && showEdgeLayer ? edgeEdit.edges : (_config$edges2 = config === null || config === void 0 ? void 0 : config.edges) !== null && _config$edges2 !== void 0 ? _config$edges2 : [];
	const getViewportCenterContentCoords = useCallback(() => {
		const w = mapAreaSize.width;
		const h = mapAreaSize.height;
		if (w <= 0 || h <= 0) return null;
		const { x: ox, y: oy } = contentOffsetRef.current;
		return {
			x: Math.round(w / 2 - ox),
			y: Math.round(h / 2 - oy)
		};
	}, [mapAreaSize.width, mapAreaSize.height]);
	/** 手机：选中图元种类后一键落在当前视口中心（与点击画布落点坐标换算一致） */
	const handlePlaceActiveAtViewportCenter = useCallback(() => {
		if (!placeMode || placeMode.type !== "scene") return;
		const center = getViewportCenterContentCoords();
		if (!center) return;
		placeSceneElementFromPaletteDrop(center.x, center.y, placeMode);
	}, [
		placeMode,
		getViewportCenterContentCoords,
		placeSceneElementFromPaletteDrop
	]);
	/** 侧栏「查看更多」：按数量批量落在画布右侧空白区（方阵网格 + gap） */
	const handleBulkAddPaletteToCanvas = useCallback((items) => {
		if (!items.length) return;
		bulkPlacePaletteItems(items);
	}, [bulkPlacePaletteItems]);
	/** 编辑态全屏策略：toggle 全屏与编辑态强绑定；locked 始终全屏顶栏；disabled 无全屏 */
	const fsMode = floorMapFullscreenMode !== null && floorMapFullscreenMode !== void 0 ? floorMapFullscreenMode : "toggle";
	const useFullTopChrome = isFloorMapView && mode === "edit" && (fsMode === "locked" || fsMode === "toggle" && isFullscreen);
	/**
	* 平面图编辑态：全屏与编辑模式强绑定（用户需求："编辑的时候就是全屏，非编辑模式就不是全屏"）。
	* 因此 toggle 策略下不再展示左下角 Dock 的进出全屏按钮——
	* 退出全屏 = 退出编辑，由顶栏「保存 / 退出编辑」入口承担。
	* locked / disabled 行为保持原样：locked 也无按钮；disabled 不允许全屏。
	*/
	const dockToggleFullscreen = void 0;
	/**
	* 平面图编辑态全屏与编辑模式双向同步：
	*   - 进 edit → 自动进入伪全屏
	*   - 退出 edit → 自动退出伪全屏
	*   - 仅 toggle 策略下生效（locked 已由 useFullTopChrome 强制；disabled 不允许全屏）
	* 强同步：依赖中包含 isFullscreen，万一被外部 / 副作用切到不一致状态会被纠正回来。
	* ESC 退出全屏的监听已在 metrics hook 中移除，避免按 ESC 后被本 effect 强制拉回来导致闪烁。
	*/
	useEffect(() => {
		if (!isFloorMapView) return;
		if (fsMode !== "toggle") return;
		if (mode === "edit" !== isFullscreen) toggleFullscreen();
	}, [
		mode,
		isFloorMapView,
		fsMode,
		isFullscreen,
		toggleFullscreen
	]);
	/** 编辑态始终保留顶栏 Tab（单画布也要能「添加画布」）；仅阅读态在单画布时可隐藏 Tab 行 */
	const showEditCompactTopBar = isFloorMapView && mode === "edit" && Boolean(config) && !useFullTopChrome;
	/**
	* 右下角视口控件：平面图下默认带出全屏按钮（若当前策略允许 toggle），
	* 并默认带出「视口锁定」按钮（与是否将视口保存到本地解耦；无持久化 key 时仅内存态，默认未锁定）。
	*/
	const viewControlsConfig = useMemo(() => {
		var _controls$showFullscr, _controls$showViewpor, _controls$showEdgesTo;
		if (!isFloorMapView) return controls;
		return _objectSpread2(_objectSpread2({}, controls), {}, {
			showFullscreen: (_controls$showFullscr = controls.showFullscreen) !== null && _controls$showFullscr !== void 0 ? _controls$showFullscr : Boolean(dockToggleFullscreen),
			showViewportLock: (_controls$showViewpor = controls.showViewportLock) !== null && _controls$showViewpor !== void 0 ? _controls$showViewpor : true,
			showEdgesToggle: showEdgeLayer ? (_controls$showEdgesTo = controls.showEdgesToggle) !== null && _controls$showEdgesTo !== void 0 ? _controls$showEdgesTo : true : controls.showEdgesToggle
		});
	}, [
		isFloorMapView,
		controls,
		dockToggleFullscreen,
		showEdgeLayer
	]);
	const topBarExtraActions = null;
	/** 从侧栏/调色板拖入画布时，地图区高亮态（用于 dragleave/over 判定） */
	const [mapPaletteDragActive, setMapPaletteDragActive] = useState(false);
	/** 全局 dragend 时清除调色板拖入高亮，避免松手在画布外仍保持 active */
	useEffect(() => {
		const clear = () => setMapPaletteDragActive(false);
		window.addEventListener("dragend", clear);
		return () => window.removeEventListener("dragend", clear);
	}, []);
	/**
	* 编辑态下的撤销 / 重做键盘快捷键：
	* - Ctrl/⌘ + Z → undo
	* - Ctrl/⌘ + Shift + Z 或 Ctrl/⌘ + Y → redo
	* 在 input/textarea/contenteditable 聚焦时跳过，避免抢走表单原生撤销。
	* 仅在容器内或容器获得焦点时响应，避免页面其它编辑区被劫持。
	*/
	useEffect(() => {
		if (!isFloorMapView || mode !== "edit" || !(config === null || config === void 0 ? void 0 : config.canvasUi.canEdit)) return;
		const onKeyDown = (e) => {
			if (!(e.ctrlKey || e.metaKey)) return;
			const key = e.key.toLowerCase();
			const isUndoCombo = key === "z" && !e.shiftKey && !e.altKey;
			const isRedoCombo = key === "z" && e.shiftKey && !e.altKey || key === "y" && !e.shiftKey && !e.altKey;
			if (!isUndoCombo && !isRedoCombo) return;
			const target = e.target;
			if (target) {
				const tag = target.tagName;
				if (tag === "INPUT" || tag === "TEXTAREA") return;
				if (target.isContentEditable) return;
			}
			const root = containerRef.current;
			if (!root) return;
			const active = document.activeElement;
			const focusInside = active ? root.contains(active) : false;
			const targetInside = target ? root.contains(target) : false;
			if (!focusInside && !targetInside) return;
			if (isUndoCombo && canUndo) {
				e.preventDefault();
				undoDraft();
			} else if (isRedoCombo && canRedo) {
				e.preventDefault();
				redoDraft();
			}
		};
		window.addEventListener("keydown", onKeyDown);
		return () => window.removeEventListener("keydown", onKeyDown);
	}, [
		isFloorMapView,
		mode,
		config === null || config === void 0 ? void 0 : config.canvasUi.canEdit,
		canUndo,
		canRedo,
		undoDraft,
		redoDraft,
		containerRef
	]);
	/**
	* 桌面端：在画布 drop 层释放拖拽时，解析 dataTransfer 并在场景坐标下落位图元。
	*/
	const handlePaletteDropOnCanvas = useCallback((e) => {
		e.preventDefault();
		e.stopPropagation();
		setMapPaletteDragActive(false);
		if (!isFloorMapView || mode !== "edit") return;
		const payload = parsePaletteDragPayload(e.dataTransfer);
		if (!payload) return;
		const { x: cx, y: cy } = sceneXYFromContentDropOffset(e.nativeEvent.offsetX, e.nativeEvent.offsetY, contentOffsetRef.current);
		placeSceneElementFromPaletteDrop(cx, cy, {
			type: "scene",
			elementKind: payload.elementKind,
			dataSourceKey: payload.dataSourceKey,
			recordId: payload.recordId,
			recordIds: payload.recordIds,
			builtinShapeKey: payload.builtinShapeKey
		});
	}, [
		isFloorMapView,
		mode,
		placeSceneElementFromPaletteDrop
	]);
	/**
	* 触摸拖拽模拟 drop：由 {@link useFloorMapPaletteTouchDrag} 在松手时调用，逻辑同桌面 drop。
	*/
	const applyPaletteTouchDropInScene = useCallback((payload, cx, cy) => {
		if (!isFloorMapView || mode !== "edit") return;
		placeSceneElementFromPaletteDrop(cx, cy, {
			type: "scene",
			elementKind: payload.elementKind,
			dataSourceKey: payload.dataSourceKey,
			recordId: payload.recordId,
			recordIds: payload.recordIds,
			builtinShapeKey: payload.builtinShapeKey
		});
	}, [
		isFloorMapView,
		mode,
		placeSceneElementFromPaletteDrop
	]);
	const paletteTouchDrag = useFloorMapPaletteTouchDrag({
		enabled: Boolean(isFloorMapView && mode === "edit" && (config === null || config === void 0 ? void 0 : config.canvasUi.canCreate)),
		mapDropLayerRef,
		contentWidth,
		contentHeight,
		contentOffsetRef,
		setMapPaletteDragActive,
		onDropInScene: applyPaletteTouchDropInScene
	});
	/** 平面图下未配置对应 kind 的 `renderItemByKind` 时的占位渲染 */
	const fallbackPlaceholderRender = useCallback((item) => {
		return renderFloorMapFallbackPlaceholder(item);
	}, []);
	/**
	* 统一图元渲染入口：平面图优先按 kind 查 `renderItemByKind`，校验绑定/配置问题后回退占位或 `renderItem`。
	*/
	const effectiveRenderItem = useCallback((item, index, options) => {
		const merged = item;
		const kind = merged === null || merged === void 0 ? void 0 : merged.elementKind;
		if (isFloorMapView && floorMapConfigProp && merged._sceneElement) {
			const kc = getElementKindConfig(floorMapConfigProp, kind);
			const issue = getSceneElementRenderIssue(merged._sceneElement, kc, dataSources);
			if (issue) return renderFloorMapRenderError(issue);
		}
		if (isFloorMapView && kind && (renderItemByKind === null || renderItemByKind === void 0 ? void 0 : renderItemByKind[kind])) try {
			return renderItemByKind[kind](item, index, options);
		} catch (err) {
			if (merged._sceneElement) onElementRenderError === null || onElementRenderError === void 0 || onElementRenderError(merged._sceneElement, err);
			return renderFloorMapRenderError(err instanceof Error ? err.message : String(err));
		}
		if (isFloorMapView && kind) return fallbackPlaceholderRender(merged);
		return renderItem(item, index, options);
	}, [
		isFloorMapView,
		floorMapConfigProp,
		dataSources,
		renderItemByKind,
		renderItem,
		fallbackPlaceholderRender,
		onElementRenderError
	]);
	/** 点击图元：编辑态同步选中 id，并透传业务 `onItemClick` */
	const handleItemClick = useCallback((item, index, e) => {
		if (isFloorMapView && mode === "edit") setSelectedId(item.id);
		onItemClick === null || onItemClick === void 0 || onItemClick(item, index, e);
	}, [
		isFloorMapView,
		mode,
		onItemClick,
		setSelectedId
	]);
	/** 对外 ref：缩放/复位/套合视口/保存布局，见 {@link PisellFloorMapLayoutRef} */
	useImperativeHandle(ref, () => ({
		zoomIn: () => {
			var _transformRef$current;
			return (_transformRef$current = transformRef.current) === null || _transformRef$current === void 0 ? void 0 : _transformRef$current.zoomIn();
		},
		zoomOut: () => {
			var _transformRef$current2;
			return (_transformRef$current2 = transformRef.current) === null || _transformRef$current2 === void 0 ? void 0 : _transformRef$current2.zoomOut();
		},
		reset: () => {
			var _transformRef$current3;
			return (_transformRef$current3 = transformRef.current) === null || _transformRef$current3 === void 0 ? void 0 : _transformRef$current3.resetTransform();
		},
		fitBounds,
		saveLayout: () => handleSave(),
		applySceneSnapshot: (next) => {
			if (mode !== "edit") return;
			commitDraft(() => next);
		}
	}), [
		fitBounds,
		handleSave,
		mode,
		commitDraft
	]);
	const showSidebar = isFloorMapView && mode === "edit" && config && dataSources;
	/** 右侧属性面板打开时给画布区留空，避免落点被挡在面板下 */
	const editPanelVisible = isFloorMapView && mode === "edit" && Boolean(editPanelParams) && Boolean(config) && Boolean(dataSources);
	/**
	* 在 map-area 上统一处理「点空白取消选中」：空白点击常落在 Transform 的 wrapper/content 上，
	* 不会冒泡到 EditableItemLayer 根节点；图元内点击由 EditableItemLayer stopPropagation，避免误清。
	*/
	const handleMapAreaBackgroundClick = useCallback((e) => {
		if (!isFloorMapView || mode !== "edit") return;
		const t = e.target;
		if (t.closest(`.pisell-floor-map-layout-editable-item`)) return;
		if (t.closest(`.pisell-floor-map-layout-edit-panel-wrap`)) return;
		if (t.closest(`.pisell-floor-map-layout-view-controls`)) return;
		if (t.closest(`.pisell-floor-map-layout-viewport-overlay`)) return;
		if (t.closest(`.pisell-floor-map-layout-place-overlay`)) return;
		if (t.closest(`.pisell-floor-map-layout-map-drop-layer`)) return;
		setSelectedId(null);
	}, [
		isFloorMapView,
		mode,
		setSelectedId
	]);
	/** 右侧手机 Drawer：open 由 props 驱动关闭动画，选中清除延后到 afterOpenChange(false) */
	const [phoneEditDrawerOpen, setPhoneEditDrawerOpen] = useState(false);
	useLayoutEffect(() => {
		if (editTopBarPhoneLayout && (editPanelParams === null || editPanelParams === void 0 ? void 0 : editPanelParams.itemId)) setPhoneEditDrawerOpen(true);
	}, [editTopBarPhoneLayout, editPanelParams === null || editPanelParams === void 0 ? void 0 : editPanelParams.itemId]);
	useEffect(() => {
		if (!editTopBarPhoneLayout || !editPanelParams) setPhoneEditDrawerOpen(false);
	}, [editTopBarPhoneLayout, editPanelParams]);
	const handlePhoneEditDrawerClose = useCallback(() => {
		setPhoneEditDrawerOpen(false);
	}, []);
	const handlePhoneEditDrawerAfterOpenChange = useCallback((open) => {
		if (!open) setSelectedId(null);
	}, [setSelectedId]);
	const handleCanvasTabChange = useCallback((id) => {
		if (id === "__pisell_floor_map_overview__") {
			/**
			* 进入全览：保留 `currentCanvasId`（用户上次浏览的画布），
			* 这样全览中点回某画布或退出后还能继承视角缓存。
			*/
			setIsOverview(true);
			setSelectedId(null);
			return;
		}
		setIsOverview(false);
		setCurrentCanvasId(id);
		setSelectedId(null);
	}, [setCurrentCanvasId, setSelectedId]);
	/** 全览态下 CanvasTabs 高亮的 id；非全览态用真实当前画布 id */
	const tabsCurrentCanvasId = isOverview ? FLOOR_MAP_OVERVIEW_TAB_ID : currentCanvasId;
	/**
	* 新版 All 视图为「单画布平铺所有画布区域」，区域单击/双击不再切换画布；
	* 切回单画布仅通过顶部 Tab，handleCanvasTabChange 负责退出全览。
	*/
	return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
		ref: containerRef,
		"data-appearance-mode": appearanceMode,
		className: classNames(FLOOR_MAP_LAYOUT_CLASS_PREFIX, className, {
			[`${FLOOR_MAP_LAYOUT_CLASS_PREFIX}-floor-map-view`]: isFloorMapView,
			[`${FLOOR_MAP_LAYOUT_CLASS_PREFIX}-mode-edit`]: isFloorMapView && mode === "edit",
			[`${FLOOR_MAP_LAYOUT_CLASS_PREFIX}--pseudo-fullscreen`]: isFullscreen
		}),
		style: _objectSpread2(_objectSpread2(_objectSpread2({
			display: "flex",
			flexDirection: "column"
		}, isFloorMapView ? { minHeight: 0 } : {}), isFloorMapView ? { ["--fmap-primary"]: token.colorPrimary } : {}), style)
	}, showEditCompactTopBar && !useFullTopChrome && /* @__PURE__ */ React.createElement(FloorMapEditTopBar, {
		mode: "compact",
		config,
		canvases: canvasesForTabs,
		currentCanvasId,
		onTabChange: handleCanvasTabChange,
		canEditTabs: config.canvasUi.canEdit,
		onAddCanvas: handleAddCanvas,
		onRemoveCanvas: handleRemoveCanvas,
		onRenameCanvas: handleRenameCanvas,
		controls,
		canvasTabsBarTrailing,
		phoneLayout: editTopBarPhoneLayout,
		onUndo: config.canvasUi.canEdit ? undoDraft : void 0,
		onRedo: config.canvasUi.canEdit ? redoDraft : void 0,
		canUndo,
		canRedo
	}), isFloorMapView && mode === "edit" && config && useFullTopChrome && /* @__PURE__ */ React.createElement(FloorMapEditTopBar, {
		mode: "full",
		config,
		canvases: canvasesForTabs,
		currentCanvasId,
		onTabChange: handleCanvasTabChange,
		canEditTabs: config.canvasUi.canEdit,
		onAddCanvas: handleAddCanvas,
		onRemoveCanvas: handleRemoveCanvas,
		onRenameCanvas: handleRenameCanvas,
		onSave: onSave ? handleSave : void 0,
		saving,
		saveError,
		layoutDirty: isLayoutDirty,
		isFullscreen,
		controls,
		extraActions: topBarExtraActions,
		canvasTabsBarTrailing,
		onUndo: config.canvasUi.canEdit ? undoDraft : void 0,
		onRedo: config.canvasUi.canEdit ? redoDraft : void 0,
		canUndo,
		canRedo,
		onExitEdit,
		onCancelEdit: onExitEdit ? handleCancelEdit : void 0,
		phoneLayout: editTopBarPhoneLayout
	}), showReadModeCanvasTabsBar && /* @__PURE__ */ React.createElement("div", {
		className: `pisell-floor-map-layout-edit-top-bar pisell-floor-map-layout-read-canvas-tabs-bar`,
		role: "toolbar",
		"aria-label": readCanvasTabsAriaLabel
	}, /* @__PURE__ */ React.createElement("div", { className: `pisell-floor-map-layout-edit-top-bar-tabs` }, /* @__PURE__ */ React.createElement(CanvasTabs, {
		canvases: canvasesForTabs,
		currentCanvasId: tabsCurrentCanvasId,
		onTabChange: handleCanvasTabChange,
		canEdit: false,
		className: `pisell-floor-map-layout-edit-top-bar-canvas-tabs`
	})), hasCanvasTabsBarTrailing ? /* @__PURE__ */ React.createElement("div", { className: `pisell-floor-map-layout-edit-top-bar-actions` }, canvasTabsBarTrailing) : null), loading && /* @__PURE__ */ React.createElement("div", { className: `pisell-floor-map-layout-loading` }, /* @__PURE__ */ React.createElement("span", null, loadingLabel)), saveError && !(isFloorMapView && mode === "edit" && config) && /* @__PURE__ */ React.createElement("div", { className: `pisell-floor-map-layout-save-error` }, typeof saveError === "string" ? saveError : saveError === null || saveError === void 0 ? void 0 : saveError.message), /* @__PURE__ */ React.createElement("div", {
		className: classNames(showSidebar && `pisell-floor-map-layout-edit-layout`, showSidebar && editTopBarPhoneLayout && `pisell-floor-map-layout-edit-layout--phone-toolbar`),
		style: {
			flex: 1,
			display: showSidebar && editTopBarPhoneLayout ? "block" : "flex",
			flexDirection: showSidebar && !editTopBarPhoneLayout ? "row" : void 0,
			minHeight: 0
		}
	}, showSidebar && /* @__PURE__ */ React.createElement(FloorMapToolbar, {
		config,
		dataSources,
		dataSourceLabels,
		canCreate: config.canvasUi.canCreate,
		placeMode,
		onPlaceModeChange: setPlaceMode,
		sceneElements: (_config$sceneElements = config.sceneElements) !== null && _config$sceneElements !== void 0 ? _config$sceneElements : [],
		renderPreviewItem: (item) => effectiveRenderItem(item, 0),
		placementPreviewSize: {
			width: FLOOR_MAP_RESOURCE_PLACE_DEFAULT.width,
			height: FLOOR_MAP_RESOURCE_PLACE_DEFAULT.height
		},
		decorationPreviewSize: 48,
		onSave: onSave && config.canvasUi.canEdit && !useFullTopChrome && toolbarShowSave !== false ? handleSave : void 0,
		saving,
		saveError,
		layoutDirty: isLayoutDirty,
		onPaletteCardPointerDown: paletteTouchDrag.handlePaletteCardPointerDown,
		consumeToolbarClickIfTouchDrag: paletteTouchDrag.consumeToolbarClickIfTouchDrag,
		phoneLayout: editTopBarPhoneLayout,
		defaultCollapsed: editTopBarPhoneLayout,
		paletteSidebarVisibleLimit,
		renderPaletteViewAllModal,
		onRequestPlaceAtCanvasCenter: editTopBarPhoneLayout ? handlePlaceActiveAtViewportCenter : void 0,
		onBulkAddPaletteToCanvas: handleBulkAddPaletteToCanvas,
		onDirectPlacePaletteItem: (item) => bulkPlacePaletteItems([item]),
		onResourcePickerConfirmPlace: (scene) => {
			const center = getViewportCenterContentCoords();
			if (!center) return false;
			return placeSceneElementFromPaletteDrop(center.x, center.y, scene);
		},
		renderResourcePickerModal
	}), /* @__PURE__ */ React.createElement("div", {
		ref: mapAreaRef,
		className: classNames(showSidebar && `pisell-floor-map-layout-map-area`, editTopBarPhoneLayout && showSidebar && `pisell-floor-map-layout-map-area--phone-fill`, editPanelVisible && !editTopBarPhoneLayout && `pisell-floor-map-layout-map-area--edit-panel-inset`),
		style: _objectSpread2(_objectSpread2({}, editTopBarPhoneLayout && showSidebar ? {
			position: "absolute",
			left: 0,
			right: 0,
			top: 0,
			bottom: 0
		} : {
			flex: 1,
			position: "relative"
		}), {}, {
			minWidth: 0,
			overflow: "hidden"
		}, isFloorMapView ? { backgroundColor: isOverview ? overviewBackgroundColor : canvasBackgroundColor } : {}),
		onClick: handleMapAreaBackgroundClick,
		onDragEnter: (e) => {
			if (!isFloorMapView || mode !== "edit") return;
			if (!isFloorMapPaletteDrag(e.dataTransfer)) return;
			e.preventDefault();
			e.stopPropagation();
			setMapPaletteDragActive(true);
		},
		onDragOver: (e) => {
			if (!isFloorMapView || mode !== "edit" || !mapPaletteDragActive) return;
			e.preventDefault();
			e.stopPropagation();
		},
		onDragLeave: (e) => {
			var _mapAreaRef$current;
			if (!mapPaletteDragActive) return;
			const related = e.relatedTarget;
			/** 图元侧栏经 Portal 挂到 body 时，移入侧栏不算离开「有效拖放区」链 */
			if (related && related instanceof Element && (related.closest(`.pisell-floor-map-layout-toolbar`) || related.closest(`.pisell-floor-map-layout-edit-panel-wrap`) || related.closest(`.pisell-floor-map-layout-toolbar-phone-drawer-root`) || related.closest(`.pisell-floor-map-layout-edit-panel-phone-drawer-root`) || related.closest(".ant-drawer") || related.closest(".pisell-lowcode-drawer"))) return;
			if (!((_mapAreaRef$current = mapAreaRef.current) === null || _mapAreaRef$current === void 0 ? void 0 : _mapAreaRef$current.contains(related))) setMapPaletteDragActive(false);
		}
	}, /* @__PURE__ */ React.createElement(TransformWrapper, _objectSpread2(_objectSpread2({
		key: transformWrapperKey,
		centerOnInit: false,
		initialScale: zoomConfig.initialScale,
		initialPositionX,
		initialPositionY,
		minScale: zoomConfig.minScale,
		maxScale: zoomConfig.maxScale,
		limitToBounds: pan.boundary !== "none"
	}, { step: zoomConfig.step }), {}, {
		panning: {
			disabled: pan.enabled === false || isInteractingWithElement || viewportLocked,
			velocityDisabled: true
		},
		wheel: { disabled: viewportLocked },
		pinch: { disabled: viewportLocked },
		doubleClick: { disabled: true },
		onTransformed: (_, state) => {
			setScale(state.scale);
			/**
			* 由 hook 内部写 lastTransformRef、判定 fitBounds 激活态。
			* 不在 onTransformed 持久化：挂载/重挂载会触发一次「初始 transform」回调，
			* 写到 localStorage 会覆盖用户上次保存的视角；
			* 持久化改由 onPanningStop / onZoomStop / onPinchingStop / onWheelStop 触发。
			*/
			notifyTransformed(state.positionX, state.positionY, state.scale);
		},
		onPanningStop: persistCurrentViewport,
		onZoomStop: persistCurrentViewport,
		onPinchingStop: persistCurrentViewport,
		onWheelStop: persistCurrentViewport
	}), (ctx) => {
		var _effectiveMapLayer$sn, _edgeEdit$drawing;
		/**
		* 不直接 transformRef.current = ctx：
		* attachTransformContext 内部会比对 ctx 实例并在每次 wrapper 重挂载时
		* 立即 schedule 一次 restore，避免「namespace 异步 → autoFit 抢跑 →
		* restore 被 autoFit 动画吃掉」导致首屏不应用缓存的时序坑。
		*/
		attachTransformContext(ctx);
		return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(TransformComponent, {
			wrapperStyle: {
				width: "100%",
				height: "100%",
				backgroundColor: isOverview ? overviewBackgroundColor : canvasBackgroundColor,
				overflow: "visible"
			},
			contentStyle: {
				width: contentWidth,
				height: contentHeight,
				minWidth: contentWidth,
				minHeight: contentHeight,
				position: "relative",
				overflow: "visible",
				backgroundColor: isOverview ? overviewBackgroundColor : canvasBackgroundColor
			}
		}, isOverview && overviewLayout && config ? /* @__PURE__ */ React.createElement(
			"div",
			/**
			* 全览模式：不渲染 MapLayer 背景 / 网格 / placeOverlay / dropLayer；
			* 每个画布的图元由 FloorMapOverview 在 contentOffset 的内部绝对定位渲染，
			* 缩放/平移/锁定/全屏/fitBounds 由外层 TransformWrapper + ViewControls 接管。
			*/
			{ style: {
				position: "absolute",
				left: contentOffsetX,
				top: contentOffsetY,
				width: overviewLayout.totalWidth,
				height: overviewLayout.totalHeight
			} },
			/* @__PURE__ */ React.createElement(FloorMapOverview, {
				overviewLayout,
				config,
				itemUnit: effectiveItemUnit,
				renderItem: effectiveRenderItem,
				viewportScale: scale,
				enableHover,
				onItemClick: handleItemClick,
				onItemHover,
				onItemLeave,
				getMergedItemWrapperStyle,
				readModeSelectedItemId,
				onSelectCanvas: handleCanvasTabChange
			})
		) : /* @__PURE__ */ React.createElement("div", {
			ref: mapContentRef,
			className: `${FLOOR_MAP_LAYOUT_CLASS_PREFIX}-map-content`,
			style: {
				position: "relative",
				width: contentWidth,
				height: contentHeight
			}
		}, /* @__PURE__ */ React.createElement(MapLayer, {
			config: effectiveMapLayer,
			contentWidth,
			contentHeight,
			cellSize,
			preferCanvasGrid: true,
			pointerEventsNone: isFloorMapView && mode === "edit"
		}), showEdgesOnCanvas ? /* @__PURE__ */ React.createElement(EdgeLayer, {
			width: contentWidth,
			height: contentHeight,
			contentOffsetX,
			contentOffsetY,
			edges: displayEdges,
			routes: edgeRoutes,
			rectsById: edgeRectsById,
			appearance: appearanceTokens,
			mode,
			selectedEdgeId: mode === "edit" ? edgeEdit.selectedEdgeId : null,
			ghostPathD: mode === "edit" ? edgeEdit.ghostPathD : null,
			onEdgeClick: mode === "edit" ? (edgeId) => edgeEdit.handleEdgeClick(edgeId) : void 0,
			viewportScale: scale,
			onSegmentDragStart: mode === "edit" ? edgeEdit.startDraggingSegment : void 0
		}) : null, isFloorMapView && mode === "edit" ? /* @__PURE__ */ React.createElement(EditableItemLayer, {
			items,
			renderItem: effectiveRenderItem,
			rowKey,
			cellSize,
			itemUnit: effectiveItemUnit,
			itemGap,
			contentWidth,
			contentHeight,
			contentOffsetX,
			contentOffsetY,
			selectedId,
			onSelect: edgeEdit.selectNode,
			onUpdateItem: handleUpdateItem,
			onUpdateItemsBatch: handleUpdateItemsBatch,
			snapStep: (_effectiveMapLayer$sn = effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.snapStep) !== null && _effectiveMapLayer$sn !== void 0 ? _effectiveMapLayer$sn : 0,
			scale,
			onInteractionChange: setIsInteractingWithElement,
			onLiveRectChange: (itemId, rect) => {
				if (!itemId || !rect) {
					edgeEdit.setPendingRectsById({});
					return;
				}
				edgeEdit.setPendingRectsById({ [itemId]: {
					left: rect.left,
					top: rect.top,
					width: rect.width,
					height: rect.height
				} });
			},
			onLiveRectsChange: (rects) => {
				edgeEdit.setPendingRectsById(rects !== null && rects !== void 0 ? rects : {});
			},
			alignGuideStroke: appearanceTokens.alignGuideStroke,
			getMergedItemWrapperStyle,
			editQuickBar
		}) : /* @__PURE__ */ React.createElement(ItemLayer, {
			items,
			renderItem: effectiveRenderItem,
			viewportScale: scale,
			rowKey,
			cellSize,
			itemUnit: effectiveItemUnit,
			itemGap,
			contentWidth,
			contentHeight,
			contentOffsetX,
			contentOffsetY,
			enableHover,
			itemHoverOutline: true,
			onItemClick: handleItemClick,
			onItemHover,
			onItemLeave,
			getMergedItemWrapperStyle,
			onInteractionChange: setIsInteractingWithElement,
			readModeSelectedItemId
		}), isFloorMapView && mode === "edit" && showEdgeLayer && selectedId && edgeEdit.selectedNodeRect ? /* @__PURE__ */ React.createElement(NodePortMarkers, {
			rect: edgeEdit.selectedNodeRect,
			contentOffsetX,
			contentOffsetY,
			viewportScale: scale,
			highlightAnchors: ((_edgeEdit$drawing = edgeEdit.drawing) === null || _edgeEdit$drawing === void 0 ? void 0 : _edgeEdit$drawing.fromInstanceId) === selectedId ? [edgeEdit.drawing.fromAnchor] : [],
			onAnchorPointerDown: (anchor) => {
				if (selectedId) edgeEdit.startDrawingFromAnchor(selectedId, anchor);
			}
		}) : null, isFloorMapView && mode === "edit" && showEdgeLayer && edgeEdit.drawing && edgeEdit.drawingTargetPreview ? /* @__PURE__ */ React.createElement(NodePortMarkers, {
			className: `${FLOOR_MAP_LAYOUT_CLASS_PREFIX}-port-markers--draw-target`,
			rect: edgeEdit.drawingTargetPreview.rect,
			contentOffsetX,
			contentOffsetY,
			viewportScale: scale,
			previewOnly: true,
			highlightAnchors: [edgeEdit.drawingTargetPreview.anchor]
		}) : null, isFloorMapView && mode === "edit" && (placeMode === null || placeMode === void 0 ? void 0 : placeMode.type) === "scene" && /* @__PURE__ */ React.createElement("div", {
			className: placeOverlayClass,
			style: {
				width: contentWidth,
				height: contentHeight
			},
			onClick: handleCanvasPlaceClick
		}), isFloorMapView && mode === "edit" && /* @__PURE__ */ React.createElement("div", {
			ref: mapDropLayerRef,
			className: classNames(`pisell-floor-map-layout-map-drop-layer`, { [`pisell-floor-map-layout-map-drop-layer-active`]: mapPaletteDragActive }),
			style: {
				width: contentWidth,
				height: contentHeight
			},
			onDragOver: (ev) => {
				ev.preventDefault();
				ev.stopPropagation();
			},
			onDrop: handlePaletteDropOnCanvas,
			onClick: () => {
				if ((placeMode === null || placeMode === void 0 ? void 0 : placeMode.type) === "scene") return;
				edgeEdit.handleMapBackgroundClick();
			}
		}))), /* @__PURE__ */ React.createElement(ViewControlsWithZoom, {
			config: viewControlsConfig,
			scale,
			isFullscreen,
			fitBoundsActive: isAtFitBounds,
			fitBounds,
			toggleFullscreen: dockToggleFullscreen,
			mapGridVisible: isOverview ? false : Boolean(effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.showGrid),
			onToggleMapGrid: isFloorMapView && !isOverview ? toggleDraftCanvasShowGrid : void 0,
			edgesLayerVisible,
			onToggleEdgesLayer: showEdgeLayer ? toggleEdgesLayerVisible : void 0,
			viewportLocked,
			onToggleViewportLock: isFloorMapView ? toggleViewportLocked : void 0,
			dockChrome: isFloorMapView,
			dockExpandedToolButtons: viewportDockToolButtons,
			dockExpandedTrailing: viewportDockTrailing,
			autoCollapseDockMs: 1e4
		}), isFloorMapView && floorMapViewportOverlay != null ? /* @__PURE__ */ React.createElement("div", { className: classNames(`${FLOOR_MAP_LAYOUT_CLASS_PREFIX}-viewport-overlay`, { [`${FLOOR_MAP_LAYOUT_CLASS_PREFIX}-viewport-overlay--bottom-start`]: viewportOverlayPlacement === "bottom-start" }, viewportOverlayClassName) }, typeof floorMapViewportOverlay === "function" ? floorMapViewportOverlay({
			items,
			dataSources,
			currentCanvasId,
			mode
		}) : floorMapViewportOverlay) : null, isFloorMapView && mode === "edit" && config && dataSources && !editTopBarPhoneLayout && (edgeEdit.selectedEdge || editPanelParams) && /* @__PURE__ */ React.createElement("div", {
			className: classNames(`pisell-floor-map-layout-edit-panel-wrap`, mapAreaSize.height > 0 && `pisell-floor-map-layout-edit-panel-wrap-constrained`),
			style: mapAreaSize.height > 0 ? { overflow: "hidden" } : void 0
		}, edgeEdit.selectedEdge ? /* @__PURE__ */ React.createElement(FloorMapEdgeEditPanel, {
			edge: edgeEdit.selectedEdge,
			config,
			onUpdate: (patch) => edgeEdit.updateEdge(edgeEdit.selectedEdge.id, patch),
			onClose: () => edgeEdit.selectEdge(null),
			onDelete: config.canvasUi.canEdit ? () => edgeEdit.deleteEdge(edgeEdit.selectedEdge.id) : void 0,
			layoutDraftDirty: isLayoutDirty
		}) : editPanelParams ? /* @__PURE__ */ React.createElement(FloorMapEditPanel, _objectSpread2(_objectSpread2({}, editPanelParams), {}, {
			config,
			dataSources,
			savingRecord
		})) : null));
	})))), editTopBarPhoneLayout && config && dataSources && (edgeEdit.selectedEdge || editPanelParams) && /* @__PURE__ */ React.createElement(Drawer, {
		placement: "right",
		open: phoneEditDrawerOpen,
		onClose: handlePhoneEditDrawerClose,
		afterOpenChange: handlePhoneEditDrawerAfterOpenChange,
		width: "min(320px, 88vw)",
		zIndex: 2002,
		maskClosable: true,
		destroyOnClose: false,
		closable: false,
		rootClassName: `pisell-floor-map-layout-edit-panel-phone-drawer-root`,
		classNames: { body: `pisell-floor-map-layout-edit-panel-phone-drawer-body` },
		styles: { body: {
			padding: 0,
			overflow: "auto",
			height: "100%"
		} },
		getContainer: typeof document !== "undefined" ? () => document.body : void 0
	}, /* @__PURE__ */ React.createElement("div", { className: classNames(`pisell-floor-map-layout-edit-panel-wrap`, `pisell-floor-map-layout-edit-panel-wrap--phone-drawer`) }, edgeEdit.selectedEdge ? /* @__PURE__ */ React.createElement(FloorMapEdgeEditPanel, {
		edge: edgeEdit.selectedEdge,
		config,
		onUpdate: (patch) => edgeEdit.updateEdge(edgeEdit.selectedEdge.id, patch),
		onClose: () => edgeEdit.selectEdge(null),
		onDelete: config.canvasUi.canEdit ? () => edgeEdit.deleteEdge(edgeEdit.selectedEdge.id) : void 0,
		layoutDraftDirty: isLayoutDirty,
		className: `pisell-floor-map-layout-edit-panel--phone-drawer`
	}) : editPanelParams ? /* @__PURE__ */ React.createElement(FloorMapEditPanel, _objectSpread2(_objectSpread2({}, editPanelParams), {}, {
		config,
		dataSources,
		savingRecord,
		className: `pisell-floor-map-layout-edit-panel--phone-drawer`
	})) : null)));
}
/** 空间布局根组件：阅读/编辑、列表与平面图双模式，ref 见 {@link PisellFloorMapLayoutRef} */
const PisellFloorMapLayout = React.forwardRef(PisellFloorMapLayoutInner);
//#endregion
export { PisellFloorMapLayout as default };
