UNPKG

@dt-workspace/react-native-heatmap

Version:

A modern, highly customizable React Native heatmap component library inspired by GitHub's contribution calendar

241 lines (231 loc) 6.41 kB
"use strict"; /** * Tooltip component for React Native Heatmap * Provides configurable tooltips with custom positioning */ import React, { useMemo } from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; // Note: screenWidth and screenHeight available if needed for future features // const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); /** * Default tooltip renderer */ const DefaultTooltipContent = ({ data, theme }) => /*#__PURE__*/_jsxs(View, { style: [styles.defaultContent, { backgroundColor: theme.colors.tooltip }], children: [/*#__PURE__*/_jsx(Text, { style: [styles.defaultText, { color: theme.colors.tooltipText }], children: data.date }), /*#__PURE__*/_jsxs(Text, { style: [styles.defaultText, { color: theme.colors.tooltipText }], children: ["Value: ", data.value] })] }); /** * Calculate tooltip position to avoid screen edges */ function calculateTooltipPosition(cellPosition, cellSize, tooltipSize, containerDimensions, preferredPosition, offset) { const cellCenterX = cellPosition.x + cellSize / 2; const cellCenterY = cellPosition.y + cellSize / 2; const tooltipWidth = tooltipSize.width; const tooltipHeight = tooltipSize.height; // Calculate positions for each direction const positions = { top: { x: cellCenterX - tooltipWidth / 2, y: cellPosition.y - tooltipHeight - offset, position: 'top' }, bottom: { x: cellCenterX - tooltipWidth / 2, y: cellPosition.y + cellSize + offset, position: 'bottom' }, left: { x: cellPosition.x - tooltipWidth - offset, y: cellCenterY - tooltipHeight / 2, position: 'left' }, right: { x: cellPosition.x + cellSize + offset, y: cellCenterY - tooltipHeight / 2, position: 'right' } }; // Check if preferred position fits if (preferredPosition && preferredPosition !== 'auto') { const pos = positions[preferredPosition]; if (pos.x >= 0 && pos.y >= 0 && pos.x + tooltipWidth <= containerDimensions.width && pos.y + tooltipHeight <= containerDimensions.height) { return pos; } } // Auto-position: find the best fit const positionOrder = ['top', 'bottom', 'right', 'left']; for (const posKey of positionOrder) { const pos = positions[posKey]; if (pos.x >= 0 && pos.y >= 0 && pos.x + tooltipWidth <= containerDimensions.width && pos.y + tooltipHeight <= containerDimensions.height) { return pos; } } // If no position fits perfectly, use top and clamp to screen const fallbackPos = positions.top; return { x: Math.max(0, Math.min(fallbackPos.x, containerDimensions.width - tooltipWidth)), y: Math.max(0, Math.min(fallbackPos.y, containerDimensions.height - tooltipHeight)), position: 'top' }; } /** * Tooltip component */ const Tooltip = ({ data, cellPosition, cellSize, config, theme, containerDimensions, visible }) => { const tooltipStyle = useMemo(() => { const baseStyle = { backgroundColor: config.backgroundColor || theme.colors.tooltip, maxWidth: 200, ...styles.tooltip, ...(config.borderRadius && { borderRadius: config.borderRadius }), ...(config.padding && { padding: config.padding }) }; if (config.shadow) { return { ...baseStyle, ...styles.shadow }; } return baseStyle; }, [config, theme]); const position = useMemo(() => { // Estimate tooltip size (this is approximate) const estimatedSize = { width: 120, height: 60 }; return calculateTooltipPosition(cellPosition, cellSize, estimatedSize, containerDimensions, config.position, config.offset || 8); }, [cellPosition, cellSize, containerDimensions, config.position, config.offset]); if (!visible) { return null; } return /*#__PURE__*/_jsxs(View, { style: [tooltipStyle, { position: 'absolute', left: position.x, top: position.y, zIndex: 1000 }], pointerEvents: "none", children: [config.content ? config.content(data) : /*#__PURE__*/_jsx(DefaultTooltipContent, { data: data, theme: theme }), config.showArrow && /*#__PURE__*/_jsx(View, { style: [styles.arrow, getArrowStyle(position.position), { backgroundColor: config.backgroundColor || theme.colors.tooltip }] })] }); }; /** * Get arrow style based on tooltip position */ function getArrowStyle(position) { const arrowSize = 6; switch (position) { case 'top': return { bottom: -arrowSize, left: '50%', marginLeft: -arrowSize / 2, borderLeftWidth: arrowSize, borderRightWidth: arrowSize, borderTopWidth: arrowSize, borderLeftColor: 'transparent', borderRightColor: 'transparent' }; case 'bottom': return { top: -arrowSize, left: '50%', marginLeft: -arrowSize / 2, borderLeftWidth: arrowSize, borderRightWidth: arrowSize, borderBottomWidth: arrowSize, borderLeftColor: 'transparent', borderRightColor: 'transparent' }; case 'left': return { right: -arrowSize, top: '50%', marginTop: -arrowSize / 2, borderTopWidth: arrowSize, borderBottomWidth: arrowSize, borderLeftWidth: arrowSize, borderTopColor: 'transparent', borderBottomColor: 'transparent' }; case 'right': return { left: -arrowSize, top: '50%', marginTop: -arrowSize / 2, borderTopWidth: arrowSize, borderBottomWidth: arrowSize, borderRightWidth: arrowSize, borderTopColor: 'transparent', borderBottomColor: 'transparent' }; default: return {}; } } const styles = StyleSheet.create({ tooltip: { borderRadius: 4, padding: 8 }, shadow: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, shadowRadius: 3.84, elevation: 5 }, defaultContent: { padding: 4 }, defaultText: { fontSize: 12, fontWeight: '500' }, arrow: { position: 'absolute', width: 0, height: 0 } }); export default Tooltip; //# sourceMappingURL=Tooltip.js.map