UNPKG

phx-react

Version:

PHX REACT

284 lines 14.9 kB
"use strict"; exports.__esModule = true; var tslib_1 = require("tslib"); var react_1 = tslib_1.__importStar(require("react")); var react_dom_1 = require("react-dom"); var PHXTooltip = function (_a) { var children = _a.children, _b = _a.className, className = _b === void 0 ? '' : _b, content = _a.content, _c = _a.contentClassName, contentClassName = _c === void 0 ? 'max-w-[18rem]' : _c, _d = _a.isDisableHover, isDisableHover = _d === void 0 ? false : _d, placement = _a.placement, _e = _a.underLine, underLine = _e === void 0 ? false : _e, _f = _a.hideDelay, hideDelay = _f === void 0 ? 200 : _f; var contentRef = (0, react_1.useRef)(null); var titleRef = (0, react_1.useRef)(null); var _g = (0, react_1.useState)({ top: 0, left: 0, width: 0, height: 0, pos: 'top', show: false, arrowOffset: 0 }), contentProps = _g[0], setContentProps = _g[1]; var hideTimeoutRef = (0, react_1.useRef)(); var isMobile = function () { return window.innerWidth <= 768; }; var getOverflowDirections = function (_a) { var height = _a.height, left = _a.left, top = _a.top, width = _a.width; var viewportWidth = window.innerWidth; var viewportHeight = window.innerHeight; var margin = isMobile() ? 20 : 10; return { isOverFlowTop: top < margin, isOverFlowLeft: left < margin, isOverFlowBottom: top + height > viewportHeight - margin, isOverFlowRight: left + width > viewportWidth - margin }; }; var getPositionProps = function (position, titleBounding, contentBounding) { var titleTop = titleBounding.top; var titleLeft = titleBounding.left; var titleWidth = titleBounding.width; var titleHeight = titleBounding.height; switch (position) { case 'top': return { width: contentBounding.width, height: contentBounding.height, top: titleTop - contentBounding.height - 10, left: titleLeft - contentBounding.width / 2 + titleWidth / 2, pos: 'top' }; case 'bottom': return { width: contentBounding.width, height: contentBounding.height, top: titleTop + titleHeight + 10, left: titleLeft - contentBounding.width / 2 + titleWidth / 2, pos: 'bottom' }; case 'left': return { width: contentBounding.width, height: contentBounding.height, top: titleTop - contentBounding.height / 2 + titleHeight / 2, left: titleLeft - contentBounding.width - 20, pos: 'left' }; case 'right': return { width: contentBounding.width, height: contentBounding.height, top: titleTop - contentBounding.height / 2 + titleHeight / 2, left: titleLeft + titleWidth + 20, pos: 'right' }; default: return { width: contentBounding.width, height: contentBounding.height, top: titleTop - contentBounding.height - 10, left: titleLeft - contentBounding.width / 2 + titleWidth / 2, pos: 'top' }; } }; var showTimeoutRef = (0, react_1.useRef)(); var calculateTooltipPosition = (0, react_1.useCallback)(function () { if (!contentRef.current || !titleRef.current) return; var titleBounding = titleRef.current.getBoundingClientRect(); var contentBounding = contentRef.current.getBoundingClientRect(); var preferredPlacement = isMobile() ? 'bottom' : placement || 'top'; var newProps = getPositionProps(preferredPlacement, titleBounding, contentBounding); var overflowDirections = getOverflowDirections(newProps); var titleCenter = titleBounding.left + titleBounding.width / 2; var titleVerticalCenter = titleBounding.top + titleBounding.height / 2; var arrowOffset = 0; if (isMobile()) { if (overflowDirections.isOverFlowBottom) { newProps = getPositionProps('top', titleBounding, contentBounding); var topOverflow = getOverflowDirections(newProps); if (topOverflow.isOverFlowTop) { newProps = tslib_1.__assign(tslib_1.__assign({}, newProps), { top: Math.max(20, Math.min(window.innerHeight - newProps.height - 20, titleBounding.top)) }); } } } else { if (placement === 'top' && overflowDirections.isOverFlowTop) { newProps = getPositionProps('bottom', titleBounding, contentBounding); } else if (placement === 'bottom' && overflowDirections.isOverFlowBottom) { newProps = getPositionProps('top', titleBounding, contentBounding); } else if (placement === 'left' && overflowDirections.isOverFlowLeft) { newProps = getPositionProps('right', titleBounding, contentBounding); } else if (placement === 'right' && overflowDirections.isOverFlowRight) { newProps = getPositionProps('left', titleBounding, contentBounding); } } var finalOverflow = getOverflowDirections(newProps); if (finalOverflow.isOverFlowLeft || finalOverflow.isOverFlowRight) { if (newProps.pos === 'top' || newProps.pos === 'bottom') { if (finalOverflow.isOverFlowRight) { var leftProps = getPositionProps('left', titleBounding, contentBounding); var leftOverflow = getOverflowDirections(leftProps); if (!leftOverflow.isOverFlowLeft) { newProps = leftProps; } else { var margin = isMobile() ? 20 : 10; newProps.left = Math.max(margin, Math.min(window.innerWidth - newProps.width - margin, newProps.left)); } } else if (finalOverflow.isOverFlowLeft) { var rightProps = getPositionProps('right', titleBounding, contentBounding); var rightOverflow = getOverflowDirections(rightProps); if (!rightOverflow.isOverFlowRight) { newProps = rightProps; } else { var margin = isMobile() ? 20 : 10; newProps.left = Math.max(margin, Math.min(window.innerWidth - newProps.width - margin, newProps.left)); } } } else { var margin = isMobile() ? 20 : 10; newProps.left = Math.max(margin, Math.min(window.innerWidth - newProps.width - margin, newProps.left)); } } if (newProps.pos === 'top' || newProps.pos === 'bottom') { arrowOffset = titleCenter - (newProps.left + newProps.width / 2); var maxOffset = newProps.width / 2 - 15; arrowOffset = Math.max(-maxOffset, Math.min(maxOffset, arrowOffset)); } if (newProps.pos === 'left' || newProps.pos === 'right') { arrowOffset = titleVerticalCenter - (newProps.top + newProps.height / 2); var maxOffset = newProps.height / 2 - 15; arrowOffset = Math.max(-maxOffset, Math.min(maxOffset, arrowOffset)); } setContentProps(function (prev) { return (tslib_1.__assign(tslib_1.__assign(tslib_1.__assign({}, prev), newProps), { arrowOffset: arrowOffset })); }); }, [placement]); var handleMouseEnter = function () { if (hideTimeoutRef.current) { clearTimeout(hideTimeoutRef.current); hideTimeoutRef.current = undefined; } if (showTimeoutRef.current) { clearTimeout(showTimeoutRef.current); } showTimeoutRef.current = setTimeout(function () { setContentProps(function (prev) { return (tslib_1.__assign(tslib_1.__assign({}, prev), { show: true })); }); setTimeout(function () { calculateTooltipPosition(); }, 10); }, 250); // <-- Độ trễ 250ms trước khi hiển thị tooltip }; var handleMouseLeave = function () { if (showTimeoutRef.current) { clearTimeout(showTimeoutRef.current); showTimeoutRef.current = undefined; } if (hideTimeoutRef.current) { clearTimeout(hideTimeoutRef.current); } hideTimeoutRef.current = setTimeout(function () { setContentProps(function (prev) { return (tslib_1.__assign(tslib_1.__assign({}, prev), { show: false })); }); }, hideDelay); }; var handleMobileClick = function () { if (isMobile()) { setContentProps(function (prev) { setTimeout(function () { calculateTooltipPosition(); }, 10); return tslib_1.__assign(tslib_1.__assign({}, prev), { show: true }); }); } }; (0, react_1.useEffect)(function () { return function () { if (hideTimeoutRef.current) { clearTimeout(hideTimeoutRef.current); } if (showTimeoutRef.current) { clearTimeout(showTimeoutRef.current); } }; }, []); (0, react_1.useEffect)(function () { var timeoutId; var isCalculating = false; var touchStartX = 0; var touchStartY = 0; var updatePosition = function () { if (contentProps.show && !isCalculating) { isCalculating = true; calculateTooltipPosition(); timeoutId = setTimeout(function () { isCalculating = false; }, 100); } }; var handleTouchStart = function (e) { if (contentProps.show && isMobile()) { touchStartX = e.touches[0].clientX; touchStartY = e.touches[0].clientY; } }; var handleTouchMove = function (e) { if (contentProps.show && isMobile()) { var touchEndX = e.touches[0].clientX; var touchEndY = e.touches[0].clientY; var deltaX = Math.abs(touchEndX - touchStartX); var deltaY = Math.abs(touchEndY - touchStartY); if (deltaX > 30 || deltaY > 30) { setContentProps(function (prev) { return (tslib_1.__assign(tslib_1.__assign({}, prev), { show: false })); }); } } }; if (contentProps.show) { updatePosition(); if (isMobile()) { document.addEventListener('touchstart', handleTouchStart, { passive: true }); document.addEventListener('touchmove', handleTouchMove, { passive: true }); } } return function () { if (timeoutId) { clearTimeout(timeoutId); } if (isMobile()) { document.removeEventListener('touchstart', handleTouchStart); document.removeEventListener('touchmove', handleTouchMove); } }; }, [contentProps.show, calculateTooltipPosition]); return (react_1["default"].createElement("div", { className: "relative w-fit text-xs font-normal text-gray-600 ".concat(underLine && 'border-b-2 border-dotted border-[#B5B5B5] pb-[1px]', " ").concat(className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave }, content && (0, react_dom_1.createPortal)(react_1["default"].createElement("div", { className: "fixed z-[999] mb-2 flex transform flex-col items-center justify-center drop-shadow-md transition-opacity duration-200 ".concat(contentProps.show ? 'visible opacity-100' : 'invisible opacity-0 pointer-events-none'), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: { top: contentProps.top, left: contentProps.left } }, react_1["default"].createElement("p", { ref: contentRef, className: "whitespace-pre-wrap break-words rounded-lg border-[1px] border-gray-300 bg-white p-2 text-xs text-gray-800 ".concat(contentClassName) }, content), react_1["default"].createElement("svg", { className: "absolute ".concat(contentProps.pos === 'top' ? '-bottom-[10px] rotate-180' : '', " ").concat(contentProps.pos === 'bottom' ? '-top-[10px]' : '', " ").concat(contentProps.pos === 'left' ? '-right-[12px] rotate-90' : '', " ").concat(contentProps.pos === 'right' ? '-left-[12px] -rotate-90' : ''), fill: 'none', height: '16', style: tslib_1.__assign({}, (contentProps.pos === 'top' || contentProps.pos === 'bottom' ? { left: "calc(50% + ".concat(contentProps.arrowOffset, "px)"), transform: "translateX(-50%) ".concat(contentProps.pos === 'top' ? 'rotate(180deg)' : '') } : { top: "calc(50% + ".concat(contentProps.arrowOffset, "px)"), transform: "translateY(-50%) ".concat(contentProps.pos === 'left' ? 'rotate(90deg)' : contentProps.pos === 'right' ? 'rotate(-90deg)' : '') })), viewBox: '0 0 20 16', width: '20', xmlns: 'http://www.w3.org/2000/svg' }, react_1["default"].createElement("path", { d: 'M10.6082 1.2699L17.3135 10.5611C17.6715 11.0571 17.3171 11.75 16.7053 11.75H3.29465C2.68295 11.75 2.32852 11.0571 2.68649 10.5611L9.39184 1.2699C9.69119 0.855102 10.3088 0.855102 10.6082 1.2699Z', fill: 'white', stroke: '#D1D5DB', strokeWidth: '1' }), react_1["default"].createElement("rect", { fill: 'white', height: '3', rx: '1.5', width: '16', x: '2', y: '10' })), !isDisableHover && react_1["default"].createElement("div", { className: 'absolute cursor-pointer top-12 h-7 w-1' })), document.body), react_1["default"].createElement("div", { ref: titleRef, onClick: handleMobileClick }, children))); }; exports["default"] = PHXTooltip; //# sourceMappingURL=ToolTip.js.map