react-multi-timezone-viewer
Version:
244 lines • 9.1 kB
JavaScript
// src/multi-timezone-viewer.tsx
import { useEffect, useRef, useState } from "react";
import { DateTime } from "luxon";
import { timeZonesNames } from "@vvo/tzdb";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
var STORAGE_KEY = "multi_tz_zones";
var allTimezones = timeZonesNames.map((tz) => {
const now = DateTime.now().setZone(tz);
const offset = now.offset / 60;
const sign = offset >= 0 ? "+" : "-";
const offsetHours = Math.floor(Math.abs(offset));
const offsetMinutes = Math.abs(now.offset % 60);
return {
label: `${tz} (UTC${sign}${offsetHours.toString().padStart(2, "0")}:${offsetMinutes.toString().padStart(2, "0")})`,
value: tz,
offset
};
}).sort((a, b) => a.offset - b.offset).map(({ label, value }) => ({ label, value }));
var getUserTimezones = () => {
const stored = localStorage.getItem(STORAGE_KEY);
return stored ? JSON.parse(stored) : [Intl.DateTimeFormat().resolvedOptions().timeZone];
};
var setUserTimezones = (zones) => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(zones));
};
var MultiTimezoneViewer = ({
datetime,
dateTimeZone,
useCrossSiteStorage = false,
customStyles = {}
}) => {
const baseTime = DateTime.fromFormat(datetime, "yyyy-MM-dd HH:mm:ss", {
zone: dateTimeZone
});
const [timezones, setTimezones] = useState([]);
const [showTooltip, setShowTooltip] = useState(false);
const [tooltipPos, setTooltipPos] = useState({ top: 0, left: 0 });
const [showDialog, setShowDialog] = useState(false);
const iframeRef = useRef(null);
const [hoveringText, setHoveringText] = useState(false);
const [hoveringTooltip, setHoveringTooltip] = useState(false);
useEffect(() => {
if (!hoveringText && !hoveringTooltip) {
const timeout = setTimeout(() => {
setShowTooltip(false);
}, 100);
return () => clearTimeout(timeout);
}
}, [hoveringText, hoveringTooltip]);
useEffect(() => {
if (useCrossSiteStorage) {
window.addEventListener("message", (e) => {
if (e.data?.type === "RETURN_TIMEZONES") {
setTimezones(e.data.zones);
}
});
const iframe = document.createElement("iframe");
iframe.src = "https://www.explisoft.com/npm/react-multi-timezone-viewer/script.html";
iframe.style.display = "none";
document.body.appendChild(iframe);
iframeRef.current = iframe;
iframe.onload = () => {
iframe.contentWindow?.postMessage("GET_TIMEZONES", "*");
};
} else {
setTimezones(getUserTimezones());
}
}, [useCrossSiteStorage]);
const handleSave = (zones) => {
setTimezones(zones);
if (useCrossSiteStorage && iframeRef.current) {
iframeRef.current.contentWindow?.postMessage({ type: "SET_TIMEZONES", zones }, "*");
} else {
setUserTimezones(zones);
}
};
const handleMouseEnter = (e) => {
const spacing = 10;
const tooltipWidth = 260;
const tooltipHeight = 200;
let left = e.clientX;
let top = e.clientY;
if (left + tooltipWidth > window.innerWidth) {
left = window.innerWidth - tooltipWidth - spacing;
}
if (top + tooltipHeight > window.innerHeight) {
top = window.innerHeight - tooltipHeight - spacing;
}
setTooltipPos({ top: top + window.scrollY + spacing, left: left + window.scrollX + spacing });
setShowTooltip(true);
};
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(
"span",
{
onMouseEnter: (e) => {
setHoveringText(true);
handleMouseEnter(e);
},
onMouseLeave: () => setHoveringText(false),
style: { display: "inline", cursor: "pointer", textDecoration: "none", ...customStyles.container },
children: baseTime.toFormat("ff")
}
),
showTooltip && /* @__PURE__ */ jsxs(
"div",
{
onMouseEnter: () => setHoveringTooltip(true),
onMouseLeave: () => setHoveringTooltip(false),
style: {
position: "absolute",
top: tooltipPos.top,
left: tooltipPos.left,
background: "#fff",
border: "1px solid #ccc",
padding: "10px",
borderRadius: "4px",
zIndex: 1e3,
boxShadow: "0 4px 8px rgba(0,0,0,0.1)",
maxWidth: "400px",
maxHeight: "300px",
overflowY: "auto",
textAlign: "left",
...customStyles.tooltip
},
children: [
/* @__PURE__ */ jsxs(
"div",
{
style: {
display: "flex",
justifyContent: "space-between",
marginBottom: "8px",
fontWeight: "bold",
...customStyles.tooltipHeader
},
children: [
/* @__PURE__ */ jsx("span", { children: "\u{1F552} Timezones" }),
/* @__PURE__ */ jsx(
"span",
{
style: { cursor: "pointer" },
onClick: () => {
setShowDialog(true);
setShowTooltip(false);
},
children: "\u2699\uFE0F"
}
)
]
}
),
/* @__PURE__ */ jsx("table", { style: { width: "100%", borderCollapse: "collapse" }, children: /* @__PURE__ */ jsx("tbody", { children: timezones.map((tz) => {
const time = baseTime.setZone(tz).toFormat("ff");
return /* @__PURE__ */ jsxs("tr", { children: [
/* @__PURE__ */ jsx("td", { style: { padding: "4px 8px", verticalAlign: "top", textAlign: "left" }, children: tz }),
/* @__PURE__ */ jsx("td", { style: { padding: "4px 8px", verticalAlign: "top", textAlign: "left" }, children: /* @__PURE__ */ jsx("strong", { children: time }) })
] }, tz);
}) }) }),
useCrossSiteStorage && iframeRef.current && /* @__PURE__ */ jsxs("div", { style: { marginTop: "10px", fontSize: "11px", color: "#888", textAlign: "right" }, children: [
"Powered by ",
/* @__PURE__ */ jsx("a", { href: "https://www.explisoft.com", target: "_blank", rel: "noopener noreferrer", children: "explisoft.com" })
] })
]
}
),
showDialog && /* @__PURE__ */ jsx(
"div",
{
style: {
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
background: "rgba(0,0,0,0.3)",
zIndex: 100,
...customStyles.dialogOverlay
},
onClick: () => setShowDialog(false),
children: /* @__PURE__ */ jsxs(
"div",
{
onClick: (e) => e.stopPropagation(),
style: {
background: "#fff",
padding: "20px",
borderRadius: "8px",
width: "400px",
maxHeight: "80vh",
overflowY: "auto",
margin: "5% auto",
textAlign: "left",
position: "relative",
...customStyles.dialogBox
},
children: [
/* @__PURE__ */ jsx(
"span",
{
onClick: () => setShowDialog(false),
style: { position: "absolute", top: 10, right: 15, cursor: "pointer" },
children: "\u274C"
}
),
/* @__PURE__ */ jsx("h3", { children: "Select Timezones" }),
/* @__PURE__ */ jsx("div", { style: { margin: "10px 0" }, children: allTimezones.map((tz) => /* @__PURE__ */ jsxs(
"label",
{
style: { display: "block", marginBottom: "5px", fontSize: "14px", cursor: "pointer", ...customStyles.checkboxLabel },
children: [
/* @__PURE__ */ jsx(
"input",
{
type: "checkbox",
checked: timezones.includes(tz.value),
onChange: (e) => {
const newZones = e.target.checked ? [...timezones, tz.value] : timezones.filter((z) => z !== tz.value);
handleSave(newZones);
}
}
),
" ",
tz.label
]
},
tz.value
)) }),
useCrossSiteStorage && iframeRef.current && /* @__PURE__ */ jsxs("div", { style: { marginTop: "10px", fontSize: "11px", color: "#888", textAlign: "right" }, children: [
"Powered by ",
/* @__PURE__ */ jsx("a", { href: "https://www.explisoft.com", target: "_blank", rel: "noopener noreferrer", children: "explisoft.com" })
] })
]
}
)
}
)
] });
};
var multi_timezone_viewer_default = MultiTimezoneViewer;
export {
multi_timezone_viewer_default as MultiTimezoneViewer
};
//# sourceMappingURL=index.mjs.map