UNPKG

react-multi-timezone-viewer

Version:
1 lines 16.6 kB
{"version":3,"sources":["../src/multi-timezone-viewer.tsx"],"sourcesContent":["// MultiTimezoneViewer.tsx\nimport React, { useEffect, useRef, useState } from 'react';\nimport { DateTime } from 'luxon';\nimport { timeZonesNames } from '@vvo/tzdb';\n\ninterface Props {\n datetime: string;\n dateTimeZone: string;\n useCrossSiteStorage?: boolean;\n customStyles?: {\n container?: React.CSSProperties;\n tooltip?: React.CSSProperties;\n tooltipHeader?: React.CSSProperties;\n dialogOverlay?: React.CSSProperties;\n dialogBox?: React.CSSProperties;\n checkboxLabel?: React.CSSProperties;\n button?: React.CSSProperties;\n };\n}\n\nconst STORAGE_KEY = 'multi_tz_zones';\n\nconst allTimezones = timeZonesNames\n .map((tz) => {\n const now = DateTime.now().setZone(tz);\n const offset = now.offset / 60;\n const sign = offset >= 0 ? '+' : '-';\n const offsetHours = Math.floor(Math.abs(offset));\n const offsetMinutes = Math.abs(now.offset % 60);\n return {\n label: `${tz} (UTC${sign}${offsetHours.toString().padStart(2, '0')}:${offsetMinutes.toString().padStart(2, '0')})`,\n value: tz,\n offset,\n };\n })\n .sort((a, b) => a.offset - b.offset) // Sort by UTC offset\n .map(({ label, value }) => ({ label, value })); // Remove offset from final object\n\n\nconst getUserTimezones = (): string[] => {\n const stored = localStorage.getItem(STORAGE_KEY);\n return stored ? JSON.parse(stored) : [Intl.DateTimeFormat().resolvedOptions().timeZone];\n};\n\nconst setUserTimezones = (zones: string[]) => {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(zones));\n};\n\nconst MultiTimezoneViewer: React.FC<Props> = ({\n datetime,\n dateTimeZone,\n useCrossSiteStorage = false,\n customStyles = {},\n}) => {\n const baseTime = DateTime.fromFormat(datetime, 'yyyy-MM-dd HH:mm:ss', {\n zone: dateTimeZone,\n });\n const [timezones, setTimezones] = useState<string[]>([]);\n const [showTooltip, setShowTooltip] = useState(false);\n const [tooltipPos, setTooltipPos] = useState({ top: 0, left: 0 });\n const [showDialog, setShowDialog] = useState(false);\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const [hoveringText, setHoveringText] = useState(false);\n const [hoveringTooltip, setHoveringTooltip] = useState(false);\n\n useEffect(() => {\n if (!hoveringText && !hoveringTooltip) {\n const timeout = setTimeout(() => {\n setShowTooltip(false);\n }, 100); // slight delay helps avoid flicker\n return () => clearTimeout(timeout);\n }\n }, [hoveringText, hoveringTooltip]);\n\n useEffect(() => {\n if (useCrossSiteStorage) {\n window.addEventListener('message', (e) => {\n if (e.data?.type === 'RETURN_TIMEZONES') {\n setTimezones(e.data.zones);\n }\n });\n\n const iframe = document.createElement('iframe');\n iframe.src = 'https://www.explisoft.com/npm/react-multi-timezone-viewer/script.html';\n iframe.style.display = 'none';\n document.body.appendChild(iframe);\n iframeRef.current = iframe;\n\n iframe.onload = () => {\n iframe.contentWindow?.postMessage('GET_TIMEZONES', '*');\n };\n } else {\n setTimezones(getUserTimezones());\n }\n }, [useCrossSiteStorage]);\n\n const handleSave = (zones: string[]) => {\n setTimezones(zones);\n if (useCrossSiteStorage && iframeRef.current) {\n iframeRef.current.contentWindow?.postMessage({ type: 'SET_TIMEZONES', zones }, '*');\n } else {\n setUserTimezones(zones);\n }\n };\n\n const handleMouseEnter = (e: React.MouseEvent) => {\n const spacing = 10;\n const tooltipWidth = 260;\n const tooltipHeight = 200;\n let left = e.clientX;\n let top = e.clientY;\n\n if (left + tooltipWidth > window.innerWidth) {\n left = window.innerWidth - tooltipWidth - spacing;\n }\n if (top + tooltipHeight > window.innerHeight) {\n top = window.innerHeight - tooltipHeight - spacing;\n }\n\n setTooltipPos({ top: top + window.scrollY + spacing, left: left + window.scrollX + spacing });\n setShowTooltip(true);\n };\n\n return (\n <>\n <span\n onMouseEnter={(e) => {\n setHoveringText(true);\n handleMouseEnter(e);\n }}\n onMouseLeave={() => setHoveringText(false)}\n style={{ display: 'inline', cursor: 'pointer', textDecoration: 'none', ...customStyles.container }}\n >\n {baseTime.toFormat('ff')}\n </span>\n\n {showTooltip && (\n <div\n onMouseEnter={() => setHoveringTooltip(true)}\n onMouseLeave={() => setHoveringTooltip(false)}\n style={{\n position: 'absolute',\n top: tooltipPos.top,\n left: tooltipPos.left,\n background: '#fff',\n border: '1px solid #ccc',\n padding: '10px',\n borderRadius: '4px',\n zIndex: 1000,\n boxShadow: '0 4px 8px rgba(0,0,0,0.1)',\n maxWidth: '400px',\n maxHeight: '300px',\n overflowY: 'auto',\n textAlign: 'left',\n ...customStyles.tooltip,\n }}\n >\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n marginBottom: '8px',\n fontWeight: 'bold',\n ...customStyles.tooltipHeader,\n }}\n >\n <span>🕒 Timezones</span>\n <span\n style={{ cursor: 'pointer' }}\n onClick={() => {\n setShowDialog(true);\n setShowTooltip(false);\n }}\n >\n ⚙️\n </span>\n </div>\n <table style={{ width: '100%', borderCollapse: 'collapse' }}>\n <tbody>\n {timezones.map((tz) => {\n const time = baseTime.setZone(tz).toFormat('ff');\n return (\n <tr key={tz}>\n <td style={{ padding: '4px 8px', verticalAlign: 'top', textAlign: 'left' }}>{tz}</td>\n <td style={{ padding: '4px 8px', verticalAlign: 'top', textAlign: 'left' }}>\n <strong>{time}</strong>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n {useCrossSiteStorage && iframeRef.current && (\n <div style={{ marginTop: '10px', fontSize: '11px', color: '#888', textAlign: 'right' }}>\n Powered by <a href=\"https://www.explisoft.com\" target=\"_blank\" rel=\"noopener noreferrer\">explisoft.com</a>\n </div>\n )}\n </div>\n )}\n\n {showDialog && (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n background: 'rgba(0,0,0,0.3)',\n zIndex: 100,\n ...customStyles.dialogOverlay,\n }}\n onClick={() => setShowDialog(false)}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n style={{\n background: '#fff',\n padding: '20px',\n borderRadius: '8px',\n width: '400px',\n maxHeight: '80vh',\n overflowY: 'auto',\n margin: '5% auto',\n textAlign: 'left',\n position: 'relative',\n ...customStyles.dialogBox,\n }}\n >\n <span\n onClick={() => setShowDialog(false)}\n style={{ position: 'absolute', top: 10, right: 15, cursor: 'pointer' }}\n >\n\n </span>\n <h3>Select Timezones</h3>\n <div style={{ margin: '10px 0' }}>\n {allTimezones.map((tz) => (\n <label\n key={tz.value}\n style={{ display: 'block', marginBottom: '5px', fontSize: '14px', cursor: 'pointer', ...customStyles.checkboxLabel }}\n >\n <input\n type=\"checkbox\"\n checked={timezones.includes(tz.value)}\n onChange={(e) => {\n const newZones = e.target.checked\n ? [...timezones, tz.value]\n : timezones.filter((z) => z !== tz.value);\n handleSave(newZones);\n }}\n />{' '}\n {tz.label}\n </label>\n ))}\n </div>\n {useCrossSiteStorage && iframeRef.current && (\n <div style={{ marginTop: '10px', fontSize: '11px', color: '#888', textAlign: 'right' }}>\n Powered by <a href=\"https://www.explisoft.com\" target=\"_blank\" rel=\"noopener noreferrer\">explisoft.com</a>\n </div>\n )}\n </div>\n </div>\n )}\n </>\n );\n};\n\nexport default MultiTimezoneViewer;\n"],"mappings":"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","names":[]}