Skip to content

Commit afa954c

Browse files
committed
fix(CompositeLayer): 修复复合图层交互问题
- FillLayer: tooltipFields 会被 buildDefaultTooltipTemplate 默认模板遮蔽,导致按字段渲染失效;改为有字段无模板时返回 undefined,使 SchemaLayer 走字段表格分支 - SchemaLayer: hover popup 进入要素即显(原先需 mousemove 才触发);Tooltip placement 由写死 top 改为 resolveHoverPlacement,贴近地图上边缘时翻转到 bottom 避免裁剪;并将该 helper 导出供其他图层复用 - IconLayer: 主图标层与降级圆点分支补 {...rest} 透传,恢复 events/zIndex/visible/autoFit 等 LayerSchema 字段(此前被静默丢弃,zIndex 等不生效) - BubbleLayer: tooltipEffect 对齐 FillLayer 改为 hover Tooltip(原先走默认 click Popup,同名 prop 语义与 FillLayer 不一致) - ArcFlowLayer: hover Tooltip 接入 resolveHoverPlacement 边缘翻转;修正 showTooltip/showNodePopup JSDoc 默认值(true→false)
1 parent 2e999a0 commit afa954c

5 files changed

Lines changed: 63 additions & 9 deletions

File tree

packages/core/src/components/CompositeLayer/ArcFlowLayer.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { LineLayer } from '../Layer/LineLayer';
44
import { PointLayer } from '../Layer/PointLayer';
55
import { Tooltip } from '../Interaction/Tooltip';
66
import { Popup } from '../Interaction/Popup';
7+
import { useScene } from '../../context/SceneContext';
8+
import { resolveHoverPlacement } from '../Layer/SchemaLayer';
79

810
/**
911
* 弧线形态
@@ -102,9 +104,9 @@ export interface ArcFlowLayerProps {
102104
nodePulse?: boolean;
103105

104106
// ===== 交互 =====
105-
/** 是否在 hover 弧线时显示 Tooltip,默认 true */
107+
/** 是否在 hover 弧线时显示 Tooltip,默认 false */
106108
showTooltip?: boolean;
107-
/** 是否在点击节点时显示 Popup,默认 true */
109+
/** 是否在点击节点时显示 Popup,默认 false */
108110
showNodePopup?: boolean;
109111
/** hover 高亮色 */
110112
activeColor?: string;
@@ -176,6 +178,7 @@ export function ArcFlowLayer({
176178
onNodeClick,
177179
style: extraStyle,
178180
}: ArcFlowLayerProps) {
181+
const scene = useScene();
179182
// 构建弧线 sourceConfig
180183
const arcSourceConfig = useMemo(() => {
181184
return sourceConfig ?? {
@@ -348,6 +351,7 @@ export function ArcFlowLayer({
348351
latitude={tooltipState.lat}
349352
visible
350353
variant="dark"
354+
placement={scene ? resolveHoverPlacement(scene, tooltipState.lng, tooltipState.lat) : 'top'}
351355
items={[
352356
{ label: '起点', value: tooltipState.from },
353357
{ label: '终点', value: tooltipState.to },

packages/core/src/components/CompositeLayer/BubbleLayer.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export interface BubbleLayerProps extends Omit<LayerSchema, 'type' | 'source' |
4848
hoverEffect?: boolean;
4949
/** 是否启用默认 click 选中反馈 */
5050
clickEffect?: boolean;
51-
/** 是否启用默认 tooltip/popup(点击气泡弹出) */
51+
/** 是否启用默认 tooltip(hover 显示),与 FillLayer 行为一致 */
5252
tooltipEffect?: boolean;
5353
/** tooltip 展示字段,不传则自动展示常见字段 */
5454
tooltipFields?: string[];
@@ -138,12 +138,15 @@ export function BubbleLayer({
138138
const defaultActive: ActiveConfig = { color: '#60a5fa', duration: 150 };
139139
const defaultSelect: SelectConfig = { color: '#1d4ed8', duration: 150 };
140140

141+
// 与 FillLayer 对齐:tooltipEffect → hover Tooltip;
142+
// 传入 tooltipFields 但无模板时,让 SchemaLayer 按字段渲染表格(popupTemplate 为 undefined)。
141143
const resolvedEvents = (() => {
142144
const origin = rest.events;
143145
if (!tooltipEffect) return origin;
144146
return {
145147
...origin,
146148
enablePopup: origin?.enablePopup ?? true,
149+
popupTrigger: origin?.popupTrigger ?? 'hover',
147150
popupFields: origin?.popupFields ?? tooltipFields,
148151
popupTemplate: origin?.popupTemplate ?? tooltipTemplate,
149152
};

packages/core/src/components/CompositeLayer/FillLayer.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,22 @@ export function FillLayer({
145145

146146
const tooltipValueField = valueField ?? colorField ?? 'value';
147147
const tooltipNameField = nameField ?? labelField;
148-
const resolvedTooltipTemplate = useMemo(
149-
() => tooltipTemplate ?? buildDefaultTooltipTemplate(tooltipNameField, tooltipValueField, percentageField),
150-
[tooltipTemplate, tooltipNameField, tooltipValueField, percentageField],
151-
);
148+
149+
// 模板决策:
150+
// 1) 用户显式传入 tooltipTemplate / events.popupTemplate → 优先使用
151+
// 2) 用户传入 popupFields 但无模板 → 不注入默认模板,让 SchemaLayer 按字段渲染表格
152+
// (否则 formatPopupContent 会进入模板分支而忽略 popupFields,导致 tooltipFields 失效)
153+
// 3) 均未指定 → 使用中文标签的默认模板
154+
const resolvedTooltipTemplate = useMemo(() => {
155+
const directTemplate = tooltipTemplate ?? events?.popupTemplate;
156+
if (directTemplate) return directTemplate;
157+
158+
// 有字段、无模板时返回 undefined,触发 SchemaLayer 的"按字段渲染表格"分支
159+
const fields = events?.popupFields ?? tooltipFields;
160+
if (fields && fields.length > 0) return undefined;
161+
162+
return buildDefaultTooltipTemplate(tooltipNameField, tooltipValueField, percentageField);
163+
}, [tooltipTemplate, events, tooltipFields, tooltipNameField, tooltipValueField, percentageField]);
152164

153165
const defaultActive: ActiveConfig = { color: '', duration: 150 };
154166
const defaultSelect: SelectConfig = { color: '#0f172a', duration: 150 };

packages/core/src/components/CompositeLayer/IconLayer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ export function IconLayer({
151151
if (shouldDegradeToPoint) {
152152
return (
153153
<PointLayer
154+
{...rest}
154155
source={source}
155156
sourceType={sourceType}
156157
sourceConfig={sourceConfig}
@@ -166,6 +167,7 @@ export function IconLayer({
166167
<>
167168
{/* 图标图层 */}
168169
<PointLayer
170+
{...rest}
169171
source={source}
170172
sourceType={sourceType}
171173
sourceConfig={sourceConfig}

packages/core/src/components/Layer/SchemaLayer.tsx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ export function SchemaLayer({ schema, scene, eventHandlers, onLayerCreated }: Sc
302302
const needsDblclick = Boolean(eventHandlersRef.current?.onDblclick);
303303
const needsUndblclick = Boolean(eventHandlersRef.current?.onUndblclick);
304304
const needsMouseMove = Boolean(eventHandlersRef.current?.onMouseMove || layerEvents?.mousemove || (popupEnabled && popupTrigger === 'hover'));
305-
const needsMouseEnter = Boolean(eventHandlersRef.current?.onMouseEnter || layerEvents?.mouseenter);
305+
const needsMouseEnter = Boolean(eventHandlersRef.current?.onMouseEnter || layerEvents?.mouseenter || (popupEnabled && popupTrigger === 'hover'));
306306
const needsMouseLeave = Boolean(eventHandlersRef.current?.onMouseLeave || layerEvents?.mouseleave || (popupEnabled && popupTrigger === 'hover'));
307307

308308
// L7 click 事件(普通单击,与 dblclick/undblclick 互斥)
@@ -350,6 +350,10 @@ export function SchemaLayer({ schema, scene, eventHandlers, onLayerCreated }: Sc
350350
layer.on('mouseenter', (evt: unknown) => {
351351
const payload = extractLayerPayload(layerId, schema.type, evt);
352352
eventHandlersRef.current?.onMouseEnter?.(payload);
353+
// hover 触发时立即显示,避免进入要素后需移动鼠标才出现的延迟
354+
if (popupEnabled && popupTrigger === 'hover') {
355+
showPopup(payload);
356+
}
353357
if (layerEvents?.mouseenter) {
354358
eventBus.emit(layerEvents.mouseenter, payload);
355359
}
@@ -431,7 +435,7 @@ export function SchemaLayer({ schema, scene, eventHandlers, onLayerCreated }: Sc
431435
latitude={popupState.lat}
432436
content={popupState.content}
433437
variant="dark"
434-
placement="top"
438+
placement={resolveHoverPlacement(scene, popupState.lng, popupState.lat)}
435439
visible={true}
436440
/>
437441
);
@@ -489,6 +493,35 @@ function sortValue(value: unknown, seen: WeakSet<object>): unknown {
489493
return value;
490494
}
491495

496+
/**
497+
* hover Tooltip 朝向:锚点贴近地图上边缘时翻转到下方,避免被裁剪。
498+
* 仅做上/下翻转(小尺寸 Tooltip 的常见需求)。
499+
*/
500+
export function resolveHoverPlacement(
501+
scene: Scene,
502+
lng: number,
503+
lat: number,
504+
): 'top' | 'bottom' {
505+
try {
506+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
507+
const mapsService = (scene as any).mapService;
508+
const container =
509+
(mapsService?.getContainer?.() as HTMLElement | undefined) ??
510+
((scene as any).getContainer?.() as HTMLElement | undefined);
511+
if (!container) return 'top';
512+
const rect = container.getBoundingClientRect();
513+
if (!rect || rect.height <= 0) return 'top';
514+
const pos = mapsService
515+
? mapsService.lngLatToContainer([lng, lat])
516+
: scene.lngLatToContainer([lng, lat]);
517+
if (!pos || !Number.isFinite(pos.x) || !Number.isFinite(pos.y)) return 'top';
518+
if (pos.y < 140) return 'bottom';
519+
} catch {
520+
// 场景/地图服务不可用时回退到上方
521+
}
522+
return 'top';
523+
}
524+
492525
/**
493526
* 格式化 Popup 内容
494527
*/

0 commit comments

Comments
 (0)