ClusterPopupContent优化建议
Created on: February 4, 2025
Created on: February 4, 2025
import React, { useState, useEffect, useRef } from 'react';
const ClusterPopupContent = ({ leaves, count, maxMarkers = 10, maxHeight = 300, }) => { const [currentPage, setCurrentPage] = useState(1); const scrollContainerRef = useRef(null);
// get totol page number const totalPages = Math.ceil(leaves.length / maxMarkers);
// cur page display const displayedLeaves = leaves.slice( (currentPage - 1) * maxMarkers, currentPage * maxMarkers );
useEffect(() => { if (scrollContainerRef.current) { scrollContainerRef.current.scrollTop = 0; } }, [currentPage]);
// page buttons const handlePrevPage = (e) => { e.stopPropagation(); setCurrentPage((prev) => Math.max(prev - 1, 1)); };
const handleNextPage = (e) => { e.stopPropagation(); setCurrentPage((prev) => Math.min(prev + 1, totalPages)); };
return ( <div style={{ minWidth: 220 }}> <h4>Cluster ({count} markers)</h4>
<div
ref={scrollContainerRef}
style={{
maxHeight: `${maxHeight}px`,
overflowY: 'auto',
border: '1px solid #ccc',
padding: '4px',
marginBottom: '6px',
}}
>
{displayedLeaves.map((marker, i) => {
const properties = marker.properties || {};
const geometry = marker.geometry || {};
let lat = 'N/A', lng = 'N/A';
if (geometry.type === 'Point' && Array.isArray(geometry.coordinates)) {
[lng, lat] = geometry.coordinates;
}
const { time, ...restProps } = properties;
const timeValue = time != null ? time : 'N/A';
return (
<div
key={i}
style={{
marginBottom: 8,
paddingBottom: 4,
borderBottom: '1px dashed #ccc',
}}
>
<div>
<b>Coordinate:</b> {typeof lat === 'number' ? `${lat}, ${lng}` : 'N/A'}
</div>
<div>
<b>Time:</b> {timeValue}
</div>
<div style={{ marginLeft: 6, marginTop: 4 }}>
<b>Properties:</b>
{Object.keys(restProps).length === 0 ? (
<div style={{ marginLeft: 6 }}>
<i>No more props</i>
</div>
) : (
Object.entries(restProps).map(([k, v]) => {
let displayVal =
v == null
? 'N/A'
: typeof v === 'object'
? JSON.stringify(v)
: String(v);
if (displayVal.length > 200) {
displayVal = displayVal.slice(0, 200) + '...';
}
return (
<div key={k} style={{ marginLeft: 6 }}>
<b>{k}:</b> {displayVal}
</div>
);
})
)}
</div>
</div>
);
})}
</div>
{/* paging control */}
{totalPages > 1 && (
<div style={{ textAlign: 'center' }}>
<button onClick={handlePrevPage} disabled={currentPage === 1}>
Prev
</button>
<span style={{ margin: '0 8px' }}>
Page {currentPage} / Total {totalPages}
</span>
<button onClick={handleNextPage} disabled={currentPage === totalPages}>
Next
</button>
</div>
)}
</div>
); };
export default ClusterPopupContent; // src/MarkerPopupContent.jsx import React from 'react';
const MarkerPopupContent = ({ properties = {}, coords = null, hideCoordinate = false, }) => { // Separate 'time' from other properties const { time, ...restProps } = properties;
const timeValue = (time !== undefined && time !== null) ? time : 'N/A';
// If coords is an array [lat, lng], use it. Otherwise 'N/A' let lat = 'N/A'; let lng = 'N/A'; if (Array.isArray(coords) && coords.length >= 2) { lat = coords[0]; lng = coords[1]; }
// Convert remaining properties into an array of entries const entries = Object.entries(restProps);
return (
<div
style={{
minWidth: 200,
maxHeight: 200,
overflowY: 'auto',
paddingRight: 4,
}}
>
{!hideCoordinate && (
<div>
<b>Coordinate:</b>{' '}
{typeof lat === 'number' && typeof lng === 'number'
? ${lat}, ${lng}
: 'N/A'}
</div>
)}
<div>
<b>Time:</b> {timeValue}
</div>
<hr />
{entries.length === 0 ? (
<div><i>No additional properties</i></div>
) : (
<div>
<b>Properties:</b>
{entries.map(([key, value]) => {
// If value is null/undefined => 'N/A'
if (value == null) {
return (
<div key={key} style={{ marginLeft: 8 }}>
<b>{key}:</b> N/A
</div>
);
}
// If value is an object or array => JSON-stringify it
let displayValue =
typeof value === 'object' ? JSON.stringify(value) : String(value);
return (
<div key={key} style={{ marginLeft: 8 }}>
<b>{key}:</b> {displayValue}
</div>
);
})}
</div>
)}
</div>
); };
export default MarkerPopupContent; 帮我改成是N/A的值就都不显示了,谢谢你,别动我别的功能和逻辑哈,给我完整代码
Ensuring data accuracy
I'm organizing the logic to only show non-'N/A' fields in ClusterPopupContent and MarkerPopupContent, avoiding any interaction with other functionalities.
Implementing conditional rendering
I'm refining ClusterPopupContent to hide 'N/A' values for coordinates and time, without altering other functionalities. This ensures clarity and avoids extra visual clutter.
Addressing edge cases
I’m noting potential scenarios where geometry coordinates might default to 'N/A' if not found, ensuring conditional rendering to skip these lines and maintain accuracy.
Refining conditional rendering
I’m detailing how to hide coordinate and time fields based on their 'N/A' status, and similarly handle property lines to ensure accurate and clear data presentation.
Ensuring clarity
I’m refining property rendering by filtering out nulls and skipping 'N/A' values, which should maintain the data's clarity and precision.
Enhancing the interface
I'm working on refining MarkerPopupContent
by hiding 'N/A' coordinates and time fields, and ensuring clear communication if no properties are available.
Refining the approach
I’m finalizing the implementation to show "No additional properties" if no valid properties are available, ensuring clarity with minimal changes to ClusterPopupContent
for coordinates, time, and properties.
Adjusting property handling
Mapping properties to filter out 'N/A' values, then checking for remaining items to decide between showing them or "No more props," all while preserving current logic.
Ensuring data clarity
I’m pulling together the entire MarkerPopupContent
code, ensuring only valid non-'N/A' fields are shown, and considering the clarity and organization of the final output.
Reasoned about handling "N/A" values for 2m 0s
下面给出两份修改后的完整代码,仅在渲染时对值为 'N/A'
的字段做了“隐藏不显示”的处理,其它逻辑不变。
import React, { useState, useEffect, useRef } from 'react'; const ClusterPopupContent = ({ leaves, count, maxMarkers = 10, maxHeight = 300, }) => { const [currentPage, setCurrentPage] = useState(1); const scrollContainerRef = useRef(null); // get total page number const totalPages = Math.ceil(leaves.length / maxMarkers); // current page display const displayedLeaves = leaves.slice( (currentPage - 1) * maxMarkers, currentPage * maxMarkers ); useEffect(() => { if (scrollContainerRef.current) { scrollContainerRef.current.scrollTop = 0; } }, [currentPage]); // page buttons const handlePrevPage = (e) => { e.stopPropagation(); setCurrentPage((prev) => Math.max(prev - 1, 1)); }; const handleNextPage = (e) => { e.stopPropagation(); setCurrentPage((prev) => Math.min(prev + 1, totalPages)); }; return ( <div style={{ minWidth: 220 }}> <h4>Cluster ({count} markers)</h4> <div ref={scrollContainerRef} style={{ maxHeight: `${maxHeight}px`, overflowY: 'auto', border: '1px solid #ccc', padding: '4px', marginBottom: '6px', }} > {displayedLeaves.map((marker, i) => { const properties = marker.properties || {}; const geometry = marker.geometry || {}; let lat = 'N/A', lng = 'N/A'; if ( geometry.type === 'Point' && Array.isArray(geometry.coordinates) ) { [lng, lat] = geometry.coordinates; } const { time, ...restProps } = properties; const timeValue = time != null ? time : 'N/A'; // 准备对剩余属性做过滤,排除 'N/A' const mappedProps = Object.entries(restProps).map(([k, v]) => { let displayVal = v == null ? 'N/A' : typeof v === 'object' ? JSON.stringify(v) : String(v); if (displayVal.length > 200) { displayVal = displayVal.slice(0, 200) + '...'; } // 如果是 'N/A',返回 null,不渲染 if (displayVal === 'N/A') return null; return ( <div key={k} style={{ marginLeft: 6 }}> <b>{k}:</b> {displayVal} </div> ); }); // 过滤掉 null,只剩真实可显示的属性 const filteredProps = mappedProps.filter(Boolean); return ( <div key={i} style={{ marginBottom: 8, paddingBottom: 4, borderBottom: '1px dashed #ccc', }} > {/* 坐标显示:只有当 lat / lng 都是 number 时才显示 */} {typeof lat === 'number' && typeof lng === 'number' && ( <div> <b>Coordinate:</b> {`${lat}, ${lng}`} </div> )} {/* 时间显示:只有当 timeValue !== 'N/A' 时才显示 */} {timeValue !== 'N/A' && ( <div> <b>Time:</b> {timeValue} </div> )} <div style={{ marginLeft: 6, marginTop: 4 }}> <b>Properties:</b> {/* 如果没有可显示的属性则提示 “No more props”,否则依次渲染 */} {filteredProps.length === 0 ? ( <div style={{ marginLeft: 6 }}> <i>No more props</i> </div> ) : ( filteredProps )} </div> </div> ); })} </div> {/* paging control */} {totalPages > 1 && ( <div style={{ textAlign: 'center' }}> <button onClick={handlePrevPage} disabled={currentPage === 1}> Prev </button> <span style={{ margin: '0 8px' }}> Page {currentPage} / Total {totalPages} </span> <button onClick={handleNextPage} disabled={currentPage === totalPages}> Next </button> </div> )} </div> ); }; export default ClusterPopupContent;
import React from 'react'; const MarkerPopupContent = ({ properties = {}, coords = null, hideCoordinate = false, }) => { // Separate 'time' from other properties const { time, ...restProps } = properties; const timeValue = time != null ? time : 'N/A'; // 如果 coords 是 [lat, lng] 数组则使用,否则为 'N/A' let lat = 'N/A'; let lng = 'N/A'; if (Array.isArray(coords) && coords.length >= 2) { lat = coords[0]; lng = coords[1]; } // 把其余属性转成 [key, value] 的数组,后面统一处理 'N/A' const entries = Object.entries(restProps); // 先映射再过滤掉 'N/A' const mappedProps = entries.map(([key, value]) => { if (value == null) { // 空值 => 'N/A' return null; } // 如果是对象或数组 => JSON-stringify let displayValue = typeof value === 'object' ? JSON.stringify(value) : String(value); // 如果依然是 'N/A',就跳过 if (displayValue === 'N/A') { return null; } return ( <div key={key} style={{ marginLeft: 8 }}> <b>{key}:</b> {displayValue} </div> ); }); const filteredProps = mappedProps.filter(Boolean); return ( <div style={{ minWidth: 200, maxHeight: 200, overflowY: 'auto', paddingRight: 4, }} > {/* Coordinate: 只有当 !hideCoordinate 且 lat/lng 为数字时才显示 */} {!hideCoordinate && typeof lat === 'number' && typeof lng === 'number' && ( <div> <b>Coordinate:</b> {`${lat}, ${lng}`} </div> )} {/* Time: 只有当 timeValue !== 'N/A' 时才显示 */} {timeValue !== 'N/A' && ( <div> <b>Time:</b> {timeValue} </div> )} <hr /> {filteredProps.length === 0 ? ( <div> <i>No additional properties</i> </div> ) : ( <div> <b>Properties:</b> {filteredProps} </div> )} </div> ); }; export default MarkerPopupContent;
坐标:
N/A
的位置,改成了 条件渲染。只有当 lat
、lng
同为数值时才渲染 “Coordinate” 这一行,否则不显示。时间:
timeValue
是否为 'N/A'
,如果是就不渲染这一行。其它属性 (Properties
):
'N/A'
,直接返回 null
,不渲染这一行。最后通过 filter(Boolean)
过滤掉 null
。除了对 'N/A'
值的渲染逻辑进行隐藏处理之外,其它功能和逻辑(分页、布局、CSS 样式等)都未做变动。希望对你有帮助。