function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
 * PisellFloorMapLayout - 空间布局视图
 * 支持两种用法：1) 仅传入 items + renderItem（原有用法）；2) 传入 floorMapConfig + dataSources + mode 使用平面图视图（含多画布/编辑态）
 */
import React, { useMemo, useRef, useImperativeHandle, useCallback, useState, useEffect, useLayoutEffect } from 'react';
import classNames from 'classnames';
import { Drawer, theme } from 'antd';
import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch';
import MapLayer from "./components/MapLayer";
import ItemLayer from "./components/ItemLayer";
import { EditableItemLayer } from "./components/EditableItemLayer";
import { FloorMapToolbar } from "./components/FloorMapToolbar";
import { FloorMapEditTopBar } from "./components/FloorMapEditTopBar";
import { CanvasTabs } from "./components/CanvasTabs";
import { FloorMapOverview } from "./components/FloorMapOverview";
import { FloorMapEditPanel } from "./components/FloorMapEditPanel";
import { ViewControlsWithZoom } from "./components/ViewControlsWithZoom";
import { useFloorMapEditState } from "./hooks/useFloorMapEditState";
import { useFloorMapTransformMetrics } from "./hooks/useFloorMapTransformMetrics";
import { DEFAULT_CELL_SIZE, gapToPx } from "./utils/layoutMeasurements";
import { renderFloorMapFallbackPlaceholder, renderFloorMapRenderError } from "./utils/floorMapFallbackRender";
import { getFloorMapAppearanceTokens } from "./appearance/floorMapAppearance";
import { getElementKindConfig, getSceneElementRenderIssue } from "./utils/floorMapElementKindUtils";
import { isFloorMapPaletteDrag, parsePaletteDragPayload, sceneXYFromContentDropOffset } from "./utils/floorMapDropUtils";
import { useFloorMapPaletteTouchDrag } from "./hooks/useFloorMapPaletteTouchDrag";
import { mergeCanvasesForTabs } from "./utils/mergeCanvasesForTabs";
import { buildFloorMapOverviewLayout } from "./utils/floorMapOverviewLayout";
import { DEFAULT_BOUNDING_PADDING } from "./hooks/floorMapViewportPersistence";
import { useFloorMapLayoutContext } from "./context/FloorMapLayoutContext";
import { mergeFloorMapLayoutPropsFromContext } from "./context/mergeFloorMapLayoutContext";
import useEngineContext from "../../hooks/useEngineContext";
import { locales } from '@pisell/utils';
import { initFloorMapLayoutLocales } from "./utils/floorMapCanvasDisplay";
import { FLOOR_MAP_LAYOUT_CLASS_PREFIX as PREFIX, FLOOR_MAP_OVERVIEW_TAB_ID, FLOOR_MAP_PHONE_EDIT_DRAWER_Z_INDEX } from "./floorMapLayoutConstants";
import "./PisellFloorMapLayout.less";

/**
 * 布局根实现（由 {@link PisellFloorMapLayout} forwardRef 包裹）。
 * 负责：上下文与 props 合并、编辑态/阅读态分支、Transform 视口、图元层与工具栏联动。
 */
function PisellFloorMapLayoutInner(props, ref) {
  var _ref, _currentCanvas$mapLay2, _currentCanvas$mapLay3, _mapLayer$backgroundC, _engineCtx$engine, _mapLayer$boundingPad, _config$sceneElements;
  var layoutCtx = useFloorMapLayoutContext();
  var p = mergeFloorMapLayoutPropsFromContext(props, layoutCtx);
  var className = p.className,
    style = p.style,
    itemsProp = p.items,
    renderItem = p.renderItem,
    rowKey = p.rowKey,
    renderItemByKind = p.renderItemByKind,
    getMergedItemWrapperStyle = p.getMergedItemWrapperStyle,
    mapLayer = p.mapLayer,
    cellSizeProp = p.cellSize,
    _p$itemUnit = p.itemUnit,
    itemUnit = _p$itemUnit === void 0 ? 'cell' : _p$itemUnit,
    _p$zoom = p.zoom,
    zoom = _p$zoom === void 0 ? {} : _p$zoom,
    _p$pan = p.pan,
    pan = _p$pan === void 0 ? {} : _p$pan,
    _p$controls = p.controls,
    controls = _p$controls === void 0 ? {} : _p$controls,
    _p$enableHover = p.enableHover,
    enableHover = _p$enableHover === void 0 ? true : _p$enableHover,
    readModeSelectedItemId = p.readModeSelectedItemId,
    onItemClick = p.onItemClick,
    onItemHover = p.onItemHover,
    onItemLeave = p.onItemLeave,
    _p$mode = p.mode,
    mode = _p$mode === void 0 ? 'read' : _p$mode,
    floorMapConfigProp = p.floorMapConfig,
    dataSources = p.dataSources,
    onSave = p.onSave,
    onExitEdit = p.onExitEdit,
    onDataSourceRecordSave = p.onDataSourceRecordSave,
    dataSourceLabels = p.dataSourceLabels,
    dataSourceFormColumns = p.dataSourceFormColumns,
    dataSourceRecordForm = p.dataSourceRecordForm,
    loading = p.loading,
    saveError = p.saveError,
    onElementRenderError = p.onElementRenderError,
    _p$toolbarShowSave = p.toolbarShowSave,
    toolbarShowSave = _p$toolbarShowSave === void 0 ? true : _p$toolbarShowSave,
    floorMapFullscreenMode = p.floorMapFullscreenMode,
    _p$appearanceMode = p.appearanceMode,
    appearanceMode = _p$appearanceMode === void 0 ? 'light' : _p$appearanceMode,
    showDataSourceRecordFormBlock = p.showDataSourceRecordFormBlock,
    floorMapViewportOverlay = p.floorMapViewportOverlay,
    _p$viewportOverlayPla = p.viewportOverlayPlacement,
    viewportOverlayPlacement = _p$viewportOverlayPla === void 0 ? 'top-start' : _p$viewportOverlayPla,
    viewportOverlayClassName = p.viewportOverlayClassName,
    _p$editTopBarPhoneLay = p.editTopBarPhoneLayout,
    editTopBarPhoneLayout = _p$editTopBarPhoneLay === void 0 ? false : _p$editTopBarPhoneLay,
    canvasTabsBarTrailing = p.canvasTabsBarTrailing,
    viewportDockToolButtons = p.viewportDockToolButtons,
    viewportDockTrailing = p.viewportDockTrailing,
    _p$viewportPersist = p.viewportPersist,
    viewportPersist = _p$viewportPersist === void 0 ? true : _p$viewportPersist,
    viewportPersistNamespace = p.viewportPersistNamespace;
  var _theme$useToken = theme.useToken(),
    token = _theme$useToken.token;
  var isFloorMapView = Boolean(floorMapConfigProp);
  var contentOffsetRef = useRef({
    x: 0,
    y: 0
  });
  var mapDropLayerRef = useRef(null);
  var transformRef = useRef(null);
  var edit = useFloorMapEditState({
    floorMapConfigProp: floorMapConfigProp,
    isFloorMapView: isFloorMapView,
    itemsProp: itemsProp,
    cellSizeProp: cellSizeProp,
    dataSources: dataSources,
    onSave: onSave,
    onDataSourceRecordSave: onDataSourceRecordSave,
    dataSourceFormColumns: dataSourceFormColumns,
    dataSourceRecordForm: dataSourceRecordForm,
    dataSourceLabels: dataSourceLabels,
    layoutPrefix: PREFIX,
    contentOffsetRef: contentOffsetRef,
    showDataSourceRecordFormBlock: showDataSourceRecordFormBlock
  });
  var config = edit.config,
    currentCanvasId = edit.currentCanvasId,
    setCurrentCanvasId = edit.setCurrentCanvasId,
    selectedId = edit.selectedId,
    setSelectedId = edit.setSelectedId,
    placeMode = edit.placeMode,
    setPlaceMode = edit.setPlaceMode,
    items = edit.items,
    saving = edit.saving,
    savingRecord = edit.savingRecord,
    handleAddCanvas = edit.handleAddCanvas,
    handleRemoveCanvas = edit.handleRemoveCanvas,
    handleRenameCanvas = edit.handleRenameCanvas,
    handleUpdateItem = edit.handleUpdateItem,
    handleCanvasPlaceClick = edit.handleCanvasPlaceClick,
    handleSave = edit.handleSave,
    placeSceneElementFromPaletteDrop = edit.placeSceneElementFromPaletteDrop,
    editPanelParams = edit.editPanelParams,
    placeOverlayClass = edit.placeOverlayClass,
    isLayoutDirty = edit.isLayoutDirty,
    toggleDraftCanvasShowGrid = edit.toggleDraftCanvasShowGrid,
    toggleSelectedLayoutLock = edit.toggleSelectedLayoutLock,
    duplicateSelectedElement = edit.duplicateSelectedElement,
    deleteSelectedElement = edit.deleteSelectedElement,
    undoDraft = edit.undoDraft,
    redoDraft = edit.redoDraft,
    canUndo = edit.canUndo,
    canRedo = edit.canRedo,
    discardDraft = edit.discardDraft;

  /**
   * 当前选中的画布配置：用于提取当前画布级 mapLayer 显式配置。
   */
  var currentCanvas = useMemo(function () {
    var _config$canvases$find;
    if (!isFloorMapView || !config || !currentCanvasId) {
      return null;
    }
    return (_config$canvases$find = config.canvases.find(function (canvas) {
      return 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 会把保存中的视觉态闪回旧值。
   */
  var handleCancelEdit = useCallback(function () {
    discardDraft();
    onExitEdit === null || onExitEdit === void 0 || onExitEdit();
  }, [discardDraft, onExitEdit]);

  /** 当前画布下的 mapLayer：全局 `mapLayer` 与画布级配置合并，供 MapLayer / 吸附等读取 */
  var effectiveMapLayer = useMemo(function () {
    var _currentCanvas$mapLay;
    if (!isFloorMapView || !currentCanvas) {
      return mapLayer;
    }
    return _objectSpread(_objectSpread({}, mapLayer !== null && mapLayer !== void 0 ? mapLayer : {}), (_currentCanvas$mapLay = currentCanvas.mapLayer) !== null && _currentCanvas$mapLay !== void 0 ? _currentCanvas$mapLay : {});
  }, [isFloorMapView, currentCanvas, mapLayer]);
  var appearanceTokens = useMemo(function () {
    return getFloorMapAppearanceTokens(appearanceMode);
  }, [appearanceMode]);
  var 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 视图。
   */
  var 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/画布数量切换；外部不感知。
   */
  var _useState = useState(false),
    _useState2 = _slicedToArray(_useState, 2),
    isOverview = _useState2[0],
    setIsOverview = _useState2[1];

  /**
   * 与下方 TransformWrapper `key` 完全一致；远程合并 padding/默认缩放回包后变化触发重挂载，
   * 单画布 ↔ 全览（All）切换也通过本 key 切换，避免视角与缓存交叉污染。
   */
  var transformWrapperKey = useMemo(function () {
    var _ref2, _zoom$defaultScale;
    if (!isFloorMapView) return 'fmap-layout';
    var 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';
    var z = (_zoom$defaultScale = zoom.defaultScale) !== null && _zoom$defaultScale !== void 0 ? _zoom$defaultScale : 1;
    if (isOverview) return "fmap-overview-bp".concat(bp, "-z").concat(z);
    return "fmap-".concat(currentCanvasId !== null && currentCanvasId !== void 0 ? currentCanvasId : 'na', "-bp").concat(bp, "-z").concat(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 使用：合并草稿画布等后的画布列表（基础列表，不含「全览」） */
  var canvasesForTabsBase = useMemo(function () {
    return config ? mergeCanvasesForTabs(config) : [];
  }, [config]);
  var engineCtx = useEngineContext();
  var engineLocale = (engineCtx === null || engineCtx === void 0 || (_engineCtx$engine = engineCtx.engine) === null || _engineCtx$engine === void 0 || (_engineCtx$engine = _engineCtx$engine.props) === null || _engineCtx$engine === void 0 ? void 0 : _engineCtx$engine.locale) || 'zh-CN';
  var hasCanvasTabsBarTrailing = canvasTabsBarTrailing != null && canvasTabsBarTrailing !== false;
  var enabledCanvasCount = useMemo(function () {
    return canvasesForTabsBase.filter(function (c) {
      return c.enabled;
    }).length;
  }, [canvasesForTabsBase]);

  /** 仅阅读态、启用画布 > 1 时，在 Tab 列表最前插入「All」伪 tab */
  var showOverviewTab = isFloorMapView && mode === 'read' && enabledCanvasCount > 1;
  var overviewTabLabel = useMemo(function () {
    initFloorMapLayoutLocales(engineLocale);
    return locales.getText('pisell-floor-map-layout.canvas-tabs.overview');
  }, [engineLocale]);

  /**
   * 给 CanvasTabs 渲染用的最终列表：阅读态多画布时在列表最前插入「All」伪 tab。
   * order 取当前最小 order - 1，保证排序后位于第一个；其它字段留空。
   */
  var canvasesForTabs = useMemo(function () {
    if (!showOverviewTab) return canvasesForTabsBase;
    var minOrder = canvasesForTabsBase.reduce(function (min, c) {
      return c.order < min ? c.order : min;
    }, Number.POSITIVE_INFINITY);
    var overviewTab = {
      id: FLOOR_MAP_OVERVIEW_TAB_ID,
      name: overviewTabLabel,
      order: Number.isFinite(minOrder) ? minOrder - 1 : -1,
      enabled: true
    };
    return [overviewTab].concat(_toConsumableArray(canvasesForTabsBase));
  }, [canvasesForTabsBase, showOverviewTab, overviewTabLabel]);

  /** 编辑态强制退出全览（多画布编辑流程不应进入只读全览界面） */
  useEffect(function () {
    if (mode !== 'read') {
      if (isOverview) setIsOverview(false);
      return;
    }
    /** 阅读态下若启用画布回落到 1 张以内，全览 Tab 已不再展示，需自动退出 */
    if (isOverview && enabledCanvasCount <= 1) {
      setIsOverview(false);
    }
  }, [mode, isOverview, enabledCanvasCount]);

  /** 阅读态是否展示画布 Tab：多启用画布或配置了 `canvasTabsBarTrailing` 时出现 */
  var showReadModeCanvasTabsBar = useMemo(function () {
    if (!isFloorMapView || mode !== 'read' || !config) return false;
    if (hasCanvasTabsBarTrailing) return true;
    return enabledCanvasCount > 1;
  }, [isFloorMapView, mode, config, hasCanvasTabsBarTrailing, enabledCanvasCount]);

  /** 阅读态画布 Tab 工具条的无障碍标签（随引擎语言初始化文案） */
  var readCanvasTabsAriaLabel = useMemo(function () {
    initFloorMapLayoutLocales(engineLocale);
    return locales.getText('pisell-floor-map-layout.read-canvas-tabs.aria');
  }, [engineLocale]);

  /** 加载遮罩上的文案 */
  var loadingLabel = useMemo(function () {
    initFloorMapLayoutLocales(engineLocale);
    return locales.getText('pisell-floor-map-layout.loading');
  }, [engineLocale]);

  /** 选中图元旁：锁定（与侧栏同字段）/ 复制 / 删除；锁定时不展开右侧属性面板 */
  var editQuickBar = useMemo(function () {
    if (!isFloorMapView || mode !== 'edit' || !(config !== null && config !== void 0 && config.canvasUi.canEdit) || !selectedId) {
      return null;
    }
    initFloorMapLayoutLocales(engineLocale);
    var row = items.find(function (i) {
      return i.id === selectedId;
    });
    if (!(row !== null && row !== 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]);
  var cellSize = cellSizeProp !== null && cellSizeProp !== void 0 ? cellSizeProp : DEFAULT_CELL_SIZE;
  /** 图元间距（像素）：由 mapLayer.gap 解析 */
  var itemGap = useMemo(function () {
    return gapToPx(effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.gap);
  }, [effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.gap]);
  /** 平面图坐标始终按像素；非平面图沿用 props `itemUnit` */
  var effectiveItemUnit = isFloorMapView ? 'pixel' : itemUnit;

  /**
   * 「全览」视图布局：把所有启用画布按各自原始尺寸（同一比例尺）紧凑装箱，
   * 主 TransformWrapper 用整体 totalWidth/Height 作为 stage 尺寸，
   * FloorMapOverview 按 areas[].x/y 把对应画布的图元绝对定位渲染。
   * 与单画布共用同一套缩放/平移/锁定/全屏/fitBounds。
   */
  var overviewLayout = useMemo(function () {
    if (!isFloorMapView || !isOverview || !config) return null;
    return buildFloorMapOverviewLayout({
      config: config,
      globalMapLayer: mapLayer,
      cellSize: cellSize,
      itemUnit: effectiveItemUnit,
      areaGap: 32
    });
  }, [isFloorMapView, isOverview, config, mapLayer, cellSize, effectiveItemUnit]);

  /**
   * 视口持久化 key：
   *   - 仅平面图模式启用（其它模式没有「画布」概念）
   *   - `viewportPersist === false` 或无 currentCanvasId 时关闭
   *   - 有 namespace 时拼成 `${ns}:${canvasId}`，隔离不同业务场景（店铺 / 平面图）
   *   - 未传 namespace 时 fallback 为仅 `${canvasId}`，小心跨场景可能共享视角
   *   - 不感知 boundingPadding / defaultScale 等渲染参数变化，避免 padding 调整导致缓存失效
   *   - 「全览」模式使用独立的 `__overview__` key，与单画布缓存互不影响
   */
  var viewportPersistKey = useMemo(function () {
    if (viewportPersist === false) return undefined;
    if (!isFloorMapView) return undefined;
    if (isOverview) {
      return viewportPersistNamespace ? "".concat(viewportPersistNamespace, ":__overview__") : '__overview__';
    }
    if (!currentCanvasId) return undefined;
    return viewportPersistNamespace ? "".concat(viewportPersistNamespace, ":").concat(currentCanvasId) : currentCanvasId;
  }, [viewportPersist, viewportPersistNamespace, isFloorMapView, isOverview, currentCanvasId]);

  /**
   * 全览模式下喂给 metrics 的虚拟 item：让 stage / fitBounds 把整体当作一张
   * 0,0,totalWidth,totalHeight 的大图元处理；不参与渲染（渲染走 FloorMapOverview）。
   * 类型上仅需 FloorMapItemBase，metrics 内部不读 `_sceneElement` 等扩展字段。
   */
  var overviewMetricsItems = useMemo(function () {
    if (!overviewLayout) return [];
    return [{
      id: '__pisell_floor_map_overview_stage__',
      x: 0,
      y: 0,
      width: overviewLayout.totalWidth,
      height: overviewLayout.totalHeight,
      shape: 'rect'
    }];
  }, [overviewLayout]);
  var metrics = useFloorMapTransformMetrics({
    items: isOverview && overviewLayout ? overviewMetricsItems : items,
    cellSize: cellSize,
    itemGap: isOverview ? 0 : itemGap,
    effectiveItemUnit: effectiveItemUnit,
    isFloorMapView: isFloorMapView,
    mode: mode,
    zoom: zoom,
    transformRef: transformRef,
    contentOffsetRef: contentOffsetRef,
    boundingPadding: isOverview ? (_mapLayer$boundingPad = mapLayer === null || mapLayer === void 0 ? void 0 : mapLayer.boundingPadding) !== null && _mapLayer$boundingPad !== void 0 ? _mapLayer$boundingPad : DEFAULT_BOUNDING_PADDING : effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.boundingPadding,
    frameResetKey: isFloorMapView ? transformWrapperKey : undefined,
    viewportPersistKey: viewportPersistKey
  });
  var containerRef = metrics.containerRef,
    mapAreaRef = metrics.mapAreaRef,
    mapAreaSize = metrics.mapAreaSize,
    contentWidth = metrics.contentWidth,
    contentHeight = metrics.contentHeight,
    contentOffsetX = metrics.contentOffsetX,
    contentOffsetY = metrics.contentOffsetY,
    initialPositionX = metrics.initialPositionX,
    initialPositionY = metrics.initialPositionY,
    zoomConfig = metrics.zoomConfig,
    scale = metrics.scale,
    setScale = metrics.setScale,
    isFullscreen = metrics.isFullscreen,
    toggleFullscreen = metrics.toggleFullscreen,
    fitBounds = metrics.fitBounds,
    isAtFitBounds = metrics.isAtFitBounds,
    notifyTransformed = metrics.notifyTransformed,
    persistCurrentViewport = metrics.persistCurrentViewport,
    attachTransformContext = metrics.attachTransformContext,
    isInteractingWithElement = metrics.isInteractingWithElement,
    setIsInteractingWithElement = metrics.setIsInteractingWithElement,
    viewportLocked = metrics.viewportLocked,
    toggleViewportLocked = metrics.toggleViewportLocked;

  /** 手机：选中图元种类后一键落在当前视口中心（与点击画布落点坐标换算一致） */
  var handlePlaceActiveAtViewportCenter = useCallback(function () {
    if (!placeMode || placeMode.type !== 'scene') return;
    var w = mapAreaSize.width;
    var h = mapAreaSize.height;
    if (w <= 0 || h <= 0) return;
    var _contentOffsetRef$cur = contentOffsetRef.current,
      ox = _contentOffsetRef$cur.x,
      oy = _contentOffsetRef$cur.y;
    var cx = Math.round(w / 2 - ox);
    var cy = Math.round(h / 2 - oy);
    placeSceneElementFromPaletteDrop(cx, cy, placeMode);
  }, [placeMode, mapAreaSize.width, mapAreaSize.height, placeSceneElementFromPaletteDrop]);

  /** 编辑态全屏策略：toggle 全屏与编辑态强绑定；locked 始终全屏顶栏；disabled 无全屏 */
  var fsMode = floorMapFullscreenMode !== null && floorMapFullscreenMode !== void 0 ? floorMapFullscreenMode : 'toggle';
  var useFullTopChrome = isFloorMapView && mode === 'edit' && (fsMode === 'locked' || fsMode === 'toggle' && isFullscreen);
  /**
   * 平面图编辑态：全屏与编辑模式强绑定（用户需求："编辑的时候就是全屏，非编辑模式就不是全屏"）。
   * 因此 toggle 策略下不再展示左下角 Dock 的进出全屏按钮——
   * 退出全屏 = 退出编辑，由顶栏「保存 / 退出编辑」入口承担。
   * locked / disabled 行为保持原样：locked 也无按钮；disabled 不允许全屏。
   */
  var dockToggleFullscreen = undefined;

  /**
   * 平面图编辑态全屏与编辑模式双向同步：
   *   - 进 edit → 自动进入伪全屏
   *   - 退出 edit → 自动退出伪全屏
   *   - 仅 toggle 策略下生效（locked 已由 useFullTopChrome 强制；disabled 不允许全屏）
   * 强同步：依赖中包含 isFullscreen，万一被外部 / 副作用切到不一致状态会被纠正回来。
   * ESC 退出全屏的监听已在 metrics hook 中移除，避免按 ESC 后被本 effect 强制拉回来导致闪烁。
   */
  useEffect(function () {
    if (!isFloorMapView) return;
    if (fsMode !== 'toggle') return;
    var shouldBeFullscreen = mode === 'edit';
    if (shouldBeFullscreen !== isFullscreen) {
      toggleFullscreen();
    }
  }, [mode, isFloorMapView, fsMode, isFullscreen, toggleFullscreen]);

  /** 编辑态始终保留顶栏 Tab（单画布也要能「添加画布」）；仅阅读态在单画布时可隐藏 Tab 行 */
  var showEditCompactTopBar = isFloorMapView && mode === 'edit' && Boolean(config) && !useFullTopChrome;

  /**
   * 右下角视口控件：平面图下默认带出全屏按钮（若当前策略允许 toggle），
   * 并默认带出「视口锁定」按钮（与是否将视口保存到本地解耦；无持久化 key 时仅内存态，默认未锁定）。
   */
  var viewControlsConfig = useMemo(function () {
    var _controls$showFullscr, _controls$showViewpor;
    if (!isFloorMapView) return controls;
    return _objectSpread(_objectSpread({}, 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
    });
  }, [isFloorMapView, controls, dockToggleFullscreen]);
  var topBarExtraActions = null;

  /** 从侧栏/调色板拖入画布时，地图区高亮态（用于 dragleave/over 判定） */
  var _useState3 = useState(false),
    _useState4 = _slicedToArray(_useState3, 2),
    mapPaletteDragActive = _useState4[0],
    setMapPaletteDragActive = _useState4[1];

  /** 全局 dragend 时清除调色板拖入高亮，避免松手在画布外仍保持 active */
  useEffect(function () {
    var clear = function clear() {
      return setMapPaletteDragActive(false);
    };
    window.addEventListener('dragend', clear);
    return function () {
      return window.removeEventListener('dragend', clear);
    };
  }, []);

  /**
   * 编辑态下的撤销 / 重做键盘快捷键：
   * - Ctrl/⌘ + Z → undo
   * - Ctrl/⌘ + Shift + Z 或 Ctrl/⌘ + Y → redo
   * 在 input/textarea/contenteditable 聚焦时跳过，避免抢走表单原生撤销。
   * 仅在容器内或容器获得焦点时响应，避免页面其它编辑区被劫持。
   */
  useEffect(function () {
    if (!isFloorMapView || mode !== 'edit' || !(config !== null && config !== void 0 && config.canvasUi.canEdit)) {
      return;
    }
    var onKeyDown = function onKeyDown(e) {
      var mod = e.ctrlKey || e.metaKey;
      if (!mod) return;
      var key = e.key.toLowerCase();
      var isUndoCombo = key === 'z' && !e.shiftKey && !e.altKey;
      var isRedoCombo = key === 'z' && e.shiftKey && !e.altKey || key === 'y' && !e.shiftKey && !e.altKey;
      if (!isUndoCombo && !isRedoCombo) return;
      var target = e.target;
      if (target) {
        var tag = target.tagName;
        if (tag === 'INPUT' || tag === 'TEXTAREA') return;
        if (target.isContentEditable) return;
      }
      var root = containerRef.current;
      if (!root) return;
      var active = document.activeElement;
      var focusInside = active ? root.contains(active) : false;
      var 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 function () {
      return window.removeEventListener('keydown', onKeyDown);
    };
  }, [isFloorMapView, mode, config === null || config === void 0 ? void 0 : config.canvasUi.canEdit, canUndo, canRedo, undoDraft, redoDraft, containerRef]);

  /**
   * 桌面端：在画布 drop 层释放拖拽时，解析 dataTransfer 并在场景坐标下落位图元。
   */
  var handlePaletteDropOnCanvas = useCallback(function (e) {
    e.preventDefault();
    e.stopPropagation();
    setMapPaletteDragActive(false);
    if (!isFloorMapView || mode !== 'edit') return;
    var payload = parsePaletteDragPayload(e.dataTransfer);
    if (!payload) return;
    var _sceneXYFromContentDr = sceneXYFromContentDropOffset(e.nativeEvent.offsetX, e.nativeEvent.offsetY, contentOffsetRef.current),
      cx = _sceneXYFromContentDr.x,
      cy = _sceneXYFromContentDr.y;
    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。
   */
  var applyPaletteTouchDropInScene = useCallback(function (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]);
  var paletteTouchDrag = useFloorMapPaletteTouchDrag({
    enabled: Boolean(isFloorMapView && mode === 'edit' && (config === null || config === void 0 ? void 0 : config.canvasUi.canCreate)),
    mapDropLayerRef: mapDropLayerRef,
    contentWidth: contentWidth,
    contentHeight: contentHeight,
    contentOffsetRef: contentOffsetRef,
    setMapPaletteDragActive: setMapPaletteDragActive,
    onDropInScene: applyPaletteTouchDropInScene
  });

  /** 平面图下未配置对应 kind 的 `renderItemByKind` 时的占位渲染 */
  var fallbackPlaceholderRender = useCallback(function (item) {
    return renderFloorMapFallbackPlaceholder(item);
  }, []);

  /**
   * 统一图元渲染入口：平面图优先按 kind 查 `renderItemByKind`，校验绑定/配置问题后回退占位或 `renderItem`。
   */
  var effectiveRenderItem = useCallback(function (item, index, options) {
    var merged = item;
    var kind = merged === null || merged === void 0 ? void 0 : merged.elementKind;
    if (isFloorMapView && floorMapConfigProp && merged._sceneElement) {
      var kc = getElementKindConfig(floorMapConfigProp, kind);
      var issue = getSceneElementRenderIssue(merged._sceneElement, kc, dataSources);
      if (issue) {
        return renderFloorMapRenderError(issue);
      }
    }
    if (isFloorMapView && kind && renderItemByKind !== null && renderItemByKind !== 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` */
  var handleItemClick = useCallback(function (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, function () {
    return {
      zoomIn: function zoomIn() {
        var _transformRef$current;
        return (_transformRef$current = transformRef.current) === null || _transformRef$current === void 0 ? void 0 : _transformRef$current.zoomIn();
      },
      zoomOut: function zoomOut() {
        var _transformRef$current2;
        return (_transformRef$current2 = transformRef.current) === null || _transformRef$current2 === void 0 ? void 0 : _transformRef$current2.zoomOut();
      },
      reset: function reset() {
        var _transformRef$current3;
        return (_transformRef$current3 = transformRef.current) === null || _transformRef$current3 === void 0 ? void 0 : _transformRef$current3.resetTransform();
      },
      fitBounds: fitBounds,
      saveLayout: function saveLayout() {
        return handleSave();
      }
    };
  }, [fitBounds, handleSave]);
  var showSidebar = isFloorMapView && mode === 'edit' && config && dataSources;

  /** 右侧属性面板打开时给画布区留空，避免落点被挡在面板下 */
  var editPanelVisible = isFloorMapView && mode === 'edit' && Boolean(editPanelParams) && Boolean(config) && Boolean(dataSources);

  /**
   * 在 map-area 上统一处理「点空白取消选中」：空白点击常落在 Transform 的 wrapper/content 上，
   * 不会冒泡到 EditableItemLayer 根节点；图元内点击由 EditableItemLayer stopPropagation，避免误清。
   */
  var handleMapAreaBackgroundClick = useCallback(function (e) {
    if (!isFloorMapView || mode !== 'edit') return;
    var t = e.target;
    if (t.closest(".".concat(PREFIX, "-editable-item"))) return;
    if (t.closest(".".concat(PREFIX, "-edit-panel-wrap"))) return;
    if (t.closest(".".concat(PREFIX, "-view-controls"))) return;
    if (t.closest(".".concat(PREFIX, "-viewport-overlay"))) return;
    if (t.closest(".".concat(PREFIX, "-place-overlay"))) return;
    if (t.closest(".".concat(PREFIX, "-map-drop-layer"))) return;
    setSelectedId(null);
  }, [isFloorMapView, mode, setSelectedId]);

  /** 右侧手机 Drawer：open 由 props 驱动关闭动画，选中清除延后到 afterOpenChange(false) */
  var _useState5 = useState(false),
    _useState6 = _slicedToArray(_useState5, 2),
    phoneEditDrawerOpen = _useState6[0],
    setPhoneEditDrawerOpen = _useState6[1];
  useLayoutEffect(function () {
    if (editTopBarPhoneLayout && editPanelParams !== null && editPanelParams !== void 0 && editPanelParams.itemId) {
      setPhoneEditDrawerOpen(true);
    }
  }, [editTopBarPhoneLayout, editPanelParams === null || editPanelParams === void 0 ? void 0 : editPanelParams.itemId]);
  useEffect(function () {
    if (!editTopBarPhoneLayout || !editPanelParams) {
      setPhoneEditDrawerOpen(false);
    }
  }, [editTopBarPhoneLayout, editPanelParams]);
  var handlePhoneEditDrawerClose = useCallback(function () {
    setPhoneEditDrawerOpen(false);
  }, []);
  var handlePhoneEditDrawerAfterOpenChange = useCallback(function (open) {
    if (!open) {
      setSelectedId(null);
    }
  }, [setSelectedId]);
  var handleCanvasTabChange = useCallback(function (id) {
    if (id === FLOOR_MAP_OVERVIEW_TAB_ID) {
      /**
       * 进入全览：保留 `currentCanvasId`（用户上次浏览的画布），
       * 这样全览中点回某画布或退出后还能继承视角缓存。
       */
      setIsOverview(true);
      setSelectedId(null);
      return;
    }
    setIsOverview(false);
    setCurrentCanvasId(id);
    setSelectedId(null);
  }, [setCurrentCanvasId, setSelectedId]);

  /** 全览态下 CanvasTabs 高亮的 id；非全览态用真实当前画布 id */
  var 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(PREFIX, className, _defineProperty(_defineProperty(_defineProperty({}, "".concat(PREFIX, "-floor-map-view"), isFloorMapView), "".concat(PREFIX, "-mode-edit"), isFloorMapView && mode === 'edit'), "".concat(PREFIX, "--pseudo-fullscreen"), isFullscreen)),
    style: _objectSpread(_objectSpread(_objectSpread({
      display: 'flex',
      flexDirection: 'column'
    }, isFloorMapView ? {
      minHeight: 0
    } : {}), isFloorMapView ? _defineProperty({}, '--fmap-primary', token.colorPrimary) : {}), style)
  }, showEditCompactTopBar && !useFullTopChrome && /*#__PURE__*/React.createElement(FloorMapEditTopBar, {
    mode: "compact",
    config: config,
    canvases: canvasesForTabs,
    currentCanvasId: currentCanvasId,
    onTabChange: handleCanvasTabChange,
    canEditTabs: config.canvasUi.canEdit,
    onAddCanvas: handleAddCanvas,
    onRemoveCanvas: handleRemoveCanvas,
    onRenameCanvas: handleRenameCanvas,
    controls: controls,
    canvasTabsBarTrailing: canvasTabsBarTrailing,
    phoneLayout: editTopBarPhoneLayout,
    onUndo: config.canvasUi.canEdit ? undoDraft : undefined,
    onRedo: config.canvasUi.canEdit ? redoDraft : undefined,
    canUndo: canUndo,
    canRedo: canRedo
  }), isFloorMapView && mode === 'edit' && config && useFullTopChrome && /*#__PURE__*/React.createElement(FloorMapEditTopBar, {
    mode: "full",
    config: config,
    canvases: canvasesForTabs,
    currentCanvasId: currentCanvasId,
    onTabChange: handleCanvasTabChange,
    canEditTabs: config.canvasUi.canEdit,
    onAddCanvas: handleAddCanvas,
    onRemoveCanvas: handleRemoveCanvas,
    onRenameCanvas: handleRenameCanvas,
    onSave: onSave ? handleSave : undefined,
    saving: saving,
    saveError: saveError,
    layoutDirty: isLayoutDirty,
    isFullscreen: isFullscreen,
    controls: controls,
    extraActions: topBarExtraActions,
    canvasTabsBarTrailing: canvasTabsBarTrailing,
    onUndo: config.canvasUi.canEdit ? undoDraft : undefined,
    onRedo: config.canvasUi.canEdit ? redoDraft : undefined,
    canUndo: canUndo,
    canRedo: canRedo,
    onExitEdit: onExitEdit,
    onCancelEdit: onExitEdit ? handleCancelEdit : undefined,
    phoneLayout: editTopBarPhoneLayout
  }), showReadModeCanvasTabsBar && /*#__PURE__*/React.createElement("div", {
    className: "".concat(PREFIX, "-edit-top-bar ").concat(PREFIX, "-read-canvas-tabs-bar"),
    role: "toolbar",
    "aria-label": readCanvasTabsAriaLabel
  }, /*#__PURE__*/React.createElement("div", {
    className: "".concat(PREFIX, "-edit-top-bar-tabs")
  }, /*#__PURE__*/React.createElement(CanvasTabs, {
    canvases: canvasesForTabs,
    currentCanvasId: tabsCurrentCanvasId,
    onTabChange: handleCanvasTabChange,
    canEdit: false,
    className: "".concat(PREFIX, "-edit-top-bar-canvas-tabs")
  })), hasCanvasTabsBarTrailing ? /*#__PURE__*/React.createElement("div", {
    className: "".concat(PREFIX, "-edit-top-bar-actions")
  }, canvasTabsBarTrailing) : null), loading && /*#__PURE__*/React.createElement("div", {
    className: "".concat(PREFIX, "-loading")
  }, /*#__PURE__*/React.createElement("span", null, loadingLabel)), saveError && !(isFloorMapView && mode === 'edit' && config) && /*#__PURE__*/React.createElement("div", {
    className: "".concat(PREFIX, "-save-error")
  }, typeof saveError === 'string' ? saveError : saveError === null || saveError === void 0 ? void 0 : saveError.message), /*#__PURE__*/React.createElement("div", {
    className: classNames(showSidebar && "".concat(PREFIX, "-edit-layout"), showSidebar && editTopBarPhoneLayout && "".concat(PREFIX, "-edit-layout--phone-toolbar")),
    style: {
      flex: 1,
      display: showSidebar && editTopBarPhoneLayout ? 'block' : 'flex',
      flexDirection: showSidebar && !editTopBarPhoneLayout ? 'row' : undefined,
      minHeight: 0
    }
  }, showSidebar && /*#__PURE__*/React.createElement(FloorMapToolbar, {
    config: config,
    dataSources: dataSources,
    dataSourceLabels: dataSourceLabels,
    canCreate: config.canvasUi.canCreate,
    placeMode: placeMode,
    onPlaceModeChange: setPlaceMode,
    sceneElements: (_config$sceneElements = config.sceneElements) !== null && _config$sceneElements !== void 0 ? _config$sceneElements : [],
    renderPreviewItem: function renderPreviewItem(item) {
      return effectiveRenderItem(item, 0);
    },
    placementPreviewSize: {
      width: 180,
      height: 120
    },
    decorationPreviewSize: 48,
    onSave: onSave && config.canvasUi.canEdit && !useFullTopChrome && toolbarShowSave !== false ? handleSave : undefined,
    saving: saving,
    saveError: saveError,
    layoutDirty: isLayoutDirty,
    onPaletteCardPointerDown: paletteTouchDrag.handlePaletteCardPointerDown,
    consumeToolbarClickIfTouchDrag: paletteTouchDrag.consumeToolbarClickIfTouchDrag,
    phoneLayout: editTopBarPhoneLayout,
    defaultCollapsed: editTopBarPhoneLayout,
    onRequestPlaceAtCanvasCenter: editTopBarPhoneLayout ? handlePlaceActiveAtViewportCenter : undefined
  }), /*#__PURE__*/React.createElement("div", {
    ref: mapAreaRef,
    className: classNames(showSidebar && "".concat(PREFIX, "-map-area"), editTopBarPhoneLayout && showSidebar && "".concat(PREFIX, "-map-area--phone-fill"), editPanelVisible && !editTopBarPhoneLayout && "".concat(PREFIX, "-map-area--edit-panel-inset")),
    style: _objectSpread(_objectSpread({}, editTopBarPhoneLayout && showSidebar ? {
      position: 'absolute',
      left: 0,
      right: 0,
      top: 0,
      bottom: 0
    } : {
      flex: 1,
      position: 'relative'
    }), {}, {
      minWidth: 0,
      overflow: 'hidden'
    }),
    onClick: handleMapAreaBackgroundClick
    /* 编辑态：侧栏 palette 拖入时 enter/over 维持 drop 合法态，leave 出地图区则取消高亮 */,
    onDragEnter: function onDragEnter(e) {
      if (!isFloorMapView || mode !== 'edit') return;
      if (!isFloorMapPaletteDrag(e.dataTransfer)) return;
      e.preventDefault();
      e.stopPropagation();
      setMapPaletteDragActive(true);
    },
    onDragOver: function onDragOver(e) {
      if (!isFloorMapView || mode !== 'edit' || !mapPaletteDragActive) return;
      e.preventDefault();
      e.stopPropagation();
    },
    onDragLeave: function onDragLeave(e) {
      var _mapAreaRef$current;
      if (!mapPaletteDragActive) return;
      var related = e.relatedTarget;
      /** 图元侧栏经 Portal 挂到 body 时，移入侧栏不算离开「有效拖放区」链 */
      if (related && related instanceof Element && (related.closest(".".concat(PREFIX, "-toolbar")) || related.closest(".".concat(PREFIX, "-edit-panel-wrap")) || related.closest(".".concat(PREFIX, "-toolbar-phone-drawer-root")) || related.closest(".".concat(PREFIX, "-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 && _mapAreaRef$current.contains(related))) {
        setMapPaletteDragActive(false);
      }
    }
  }, /*#__PURE__*/React.createElement(TransformWrapper, {
    key: transformWrapperKey,
    centerOnInit: false,
    initialScale: zoomConfig.initialScale,
    initialPositionX: initialPositionX,
    initialPositionY: initialPositionY,
    minScale: zoomConfig.minScale,
    maxScale: zoomConfig.maxScale
    /**
     * 库默认 limitToBounds=true，平移会被限制在内容包盒内。
     * pan.boundary === 'none' 时关闭限制，可一直拖动画布（视觉上类似无限大底图）。
     * 'clamp' / 'bounce' / 未传：保持限制（与历史行为一致）。
     */,
    limitToBounds: pan.boundary !== 'none'
    // v3 类型定义未包含 step，库仍支持缩放步进时使用
    ,
    step: zoomConfig.step,
    /**
     * 视口锁定：把所有库内置交互写入 transform 的入口都关掉。
     * - panning.disabled：拖拽
     * - wheel.disabled：滚轮缩放
     * - pinch.disabled：触摸双指缩放
     * - doubleClick.disabled：本就为 true（业务自定义双击行为）
     * 程序化 setTransform / 缓存恢复仍然有效，所以锁定时切换画布回来仍能贴回锁定时的视角。
     */
    panning: {
      disabled: pan.enabled === false || isInteractingWithElement || viewportLocked,
      velocityDisabled: true
    },
    wheel: {
      disabled: viewportLocked
    },
    pinch: {
      disabled: viewportLocked
    },
    doubleClick: {
      disabled: true
    },
    onTransformed: function 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
  }, function (ctx) {
    var _effectiveMapLayer$sn;
    /**
     * 不直接 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__*/
    /**
     * 全览模式：不渲染 MapLayer 背景 / 网格 / placeOverlay / dropLayer；
     * 每个画布的图元由 FloorMapOverview 在 contentOffset 的内部绝对定位渲染，
     * 缩放/平移/锁定/全屏/fitBounds 由外层 TransformWrapper + ViewControls 接管。
     */
    React.createElement("div", {
      style: {
        position: 'absolute',
        left: contentOffsetX,
        top: contentOffsetY,
        width: overviewLayout.totalWidth,
        height: overviewLayout.totalHeight
      }
    }, /*#__PURE__*/React.createElement(FloorMapOverview, {
      overviewLayout: overviewLayout,
      config: config,
      itemUnit: effectiveItemUnit,
      renderItem: effectiveRenderItem
      /**
       * 与单画布同一份 `scale`：让 All 视图里各区域 ItemLayer
       * 的 renderItem 能拿到 `viewportScale`，预约 / 桌位卡才会
       * 按「视觉短边」降档到 tooltip-time 小卡片模式。
       */,
      viewportScale: scale,
      enableHover: enableHover,
      onItemClick: handleItemClick,
      onItemHover: onItemHover,
      onItemLeave: onItemLeave,
      getMergedItemWrapperStyle: getMergedItemWrapperStyle,
      readModeSelectedItemId: readModeSelectedItemId
    })) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(MapLayer, {
      config: effectiveMapLayer,
      contentWidth: contentWidth,
      contentHeight: contentHeight,
      cellSize: cellSize,
      preferCanvasGrid: true,
      pointerEventsNone: isFloorMapView && mode === 'edit'
    }), isFloorMapView && mode === 'edit' ? /*#__PURE__*/React.createElement(EditableItemLayer, {
      items: items,
      renderItem: effectiveRenderItem,
      rowKey: rowKey,
      cellSize: cellSize,
      itemUnit: effectiveItemUnit,
      itemGap: itemGap,
      contentWidth: contentWidth,
      contentHeight: contentHeight,
      contentOffsetX: contentOffsetX,
      contentOffsetY: contentOffsetY,
      selectedId: selectedId,
      onSelect: setSelectedId,
      onUpdateItem: handleUpdateItem,
      snapStep: (_effectiveMapLayer$sn = effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.snapStep) !== null && _effectiveMapLayer$sn !== void 0 ? _effectiveMapLayer$sn : 0,
      scale: scale,
      onInteractionChange: setIsInteractingWithElement,
      getMergedItemWrapperStyle: getMergedItemWrapperStyle,
      editQuickBar: editQuickBar
    }) : /*#__PURE__*/React.createElement(ItemLayer, {
      items: items,
      renderItem: effectiveRenderItem,
      viewportScale: scale,
      rowKey: rowKey,
      cellSize: cellSize,
      itemUnit: effectiveItemUnit,
      itemGap: itemGap,
      contentWidth: contentWidth,
      contentHeight: contentHeight,
      contentOffsetX: contentOffsetX,
      contentOffsetY: contentOffsetY,
      enableHover: enableHover,
      itemHoverOutline: true,
      onItemClick: handleItemClick,
      onItemHover: onItemHover,
      onItemLeave: onItemLeave,
      getMergedItemWrapperStyle: getMergedItemWrapperStyle,
      onInteractionChange: setIsInteractingWithElement,
      readModeSelectedItemId: readModeSelectedItemId
    }), 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("".concat(PREFIX, "-map-drop-layer"), _defineProperty({}, "".concat(PREFIX, "-map-drop-layer-active"), mapPaletteDragActive)),
      style: {
        width: contentWidth,
        height: contentHeight
      },
      onDragOver: function onDragOver(ev) {
        ev.preventDefault();
        ev.stopPropagation();
      },
      onDrop: handlePaletteDropOnCanvas
    }))), /*#__PURE__*/React.createElement(ViewControlsWithZoom, {
      config: viewControlsConfig,
      scale: scale,
      isFullscreen: isFullscreen,
      fitBoundsActive: isAtFitBounds,
      fitBounds: fitBounds,
      toggleFullscreen: dockToggleFullscreen
      /**
       * 全览模式无 MapLayer / 网格，对应按钮通过传 undefined 隐藏；
       * 锁定、全屏、fitBounds、缩放与单画布共用同一套状态。
       */,
      mapGridVisible: isOverview ? false : Boolean(effectiveMapLayer === null || effectiveMapLayer === void 0 ? void 0 : effectiveMapLayer.showGrid),
      onToggleMapGrid: isFloorMapView && !isOverview ? toggleDraftCanvasShowGrid : undefined,
      viewportLocked: viewportLocked,
      onToggleViewportLock: isFloorMapView ? toggleViewportLocked : undefined,
      dockChrome: isFloorMapView,
      dockExpandedToolButtons: viewportDockToolButtons,
      dockExpandedTrailing: viewportDockTrailing,
      autoCollapseDockMs: 10000
    }), isFloorMapView && floorMapViewportOverlay != null ? /*#__PURE__*/React.createElement("div", {
      className: classNames("".concat(PREFIX, "-viewport-overlay"), _defineProperty({}, "".concat(PREFIX, "-viewport-overlay--bottom-start"), viewportOverlayPlacement === 'bottom-start'), viewportOverlayClassName)
    }, typeof floorMapViewportOverlay === 'function' ? floorMapViewportOverlay({
      items: items,
      dataSources: dataSources,
      currentCanvasId: currentCanvasId,
      mode: mode
    }) : floorMapViewportOverlay) : null, isFloorMapView && mode === 'edit' && editPanelParams && config && dataSources && !editTopBarPhoneLayout && /*#__PURE__*/React.createElement("div", {
      className: classNames("".concat(PREFIX, "-edit-panel-wrap"), mapAreaSize.height > 0 && "".concat(PREFIX, "-edit-panel-wrap-constrained")),
      style: mapAreaSize.height > 0 ? {
        height: mapAreaSize.height - 32,
        maxHeight: mapAreaSize.height - 32,
        overflow: 'hidden',
        top: 16,
        transform: 'none'
      } : undefined
    }, /*#__PURE__*/React.createElement(FloorMapEditPanel, _extends({}, editPanelParams, {
      config: config,
      dataSources: dataSources,
      savingRecord: savingRecord
    }))));
  })))), editTopBarPhoneLayout && editPanelParams && config && dataSources && /*#__PURE__*/React.createElement(Drawer, {
    placement: "right",
    open: phoneEditDrawerOpen,
    onClose: handlePhoneEditDrawerClose,
    afterOpenChange: handlePhoneEditDrawerAfterOpenChange,
    width: "min(320px, 88vw)",
    zIndex: FLOOR_MAP_PHONE_EDIT_DRAWER_Z_INDEX,
    maskClosable: true,
    destroyOnClose: false,
    closable: false,
    rootClassName: "".concat(PREFIX, "-edit-panel-phone-drawer-root"),
    classNames: {
      body: "".concat(PREFIX, "-edit-panel-phone-drawer-body")
    },
    styles: {
      body: {
        padding: 0,
        overflow: 'auto',
        height: '100%'
      }
    },
    getContainer: typeof document !== 'undefined' ? function () {
      return document.body;
    } : undefined
  }, /*#__PURE__*/React.createElement("div", {
    className: classNames("".concat(PREFIX, "-edit-panel-wrap"), "".concat(PREFIX, "-edit-panel-wrap--phone-drawer"))
  }, /*#__PURE__*/React.createElement(FloorMapEditPanel, _extends({}, editPanelParams, {
    config: config,
    dataSources: dataSources,
    savingRecord: savingRecord,
    className: "".concat(PREFIX, "-edit-panel--phone-drawer")
  })))));
}

/** 空间布局根组件：阅读/编辑、列表与平面图双模式，ref 见 {@link PisellFloorMapLayoutRef} */
var PisellFloorMapLayout = /*#__PURE__*/React.forwardRef(PisellFloorMapLayoutInner);
export { PisellFloorMapLayout };
export default PisellFloorMapLayout;