Created
October 9, 2024 07:56
-
-
Save BorisAnthony/783de124610cd4de6e3d6491f8f957fe to your computer and use it in GitHub Desktop.
Übersicht "TimeZoneStrip" JSX widget
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Übersicht "TimeZoneStrip" JSX widget | |
* [email protected] / 2024-10-09 | |
* MIT Licensed | |
* Based on https://www.reddit.com/r/Ubersicht/comments/1fdp5bu/widget_based_on_the_worldtimebuddy/ | |
*/ | |
import { css, run } from "uebersicht"; | |
const timezones = [ | |
{ name: "JST", timezone: "Asia/Tokyo" }, | |
{ name: "CET", timezone: "Europe/Berlin" }, | |
{ name: "UTC", timezone: "UTC" }, | |
{ name: "EST", timezone: "America/New_York" }, | |
{ name: "PST", timezone: "America/Los_Angeles" }, | |
]; | |
const timezones_count = timezones.length; | |
export const command = async (dispatch) => { | |
const times = await Promise.all( | |
timezones.map(async (tz) => { | |
const date = await run(`TZ=${tz.timezone} date +'%H %d %b'`); | |
const [hour, day, month] = date.trim().split(" "); | |
return { ...tz, hour: parseInt(hour), day, month }; | |
}) | |
); | |
dispatch({ type: "UPDATE_TIMES", times }); | |
}; | |
export const refreshFrequency = 60000; // Update every minute | |
export const initialState = { times: [] }; | |
export const updateState = (event, previousState) => { | |
if (event.type === "UPDATE_TIMES") { | |
return { ...previousState, times: event.times }; | |
} | |
return previousState; | |
}; | |
const HourBox = ({ content, type, isCurrentHour }) => { | |
let boxStyle = hourBox; | |
let textColor = ""; | |
switch (type) { | |
case "date": | |
boxStyle += ` ${dateBox}`; | |
textColor = `rgb(255 255 255 / ${isCurrentHour ? 1 : 0.8})`; | |
break; | |
case "earlyMorning": | |
boxStyle += ` ${earlyMorning}`; | |
textColor = `rgb(255 255 255 / ${isCurrentHour ? 1 : 0.5})`; | |
break; | |
case "morning": | |
boxStyle += ` ${morning}`; | |
textColor = `rgb(255 255 255 / ${isCurrentHour ? 1 : 0.4})`; | |
break; | |
case "dayNight": | |
boxStyle += ` ${dayNight}`; | |
textColor = `rgb(255 255 255 / ${isCurrentHour ? 1 : 0.7})`; | |
break; | |
case "evening": | |
// boxStyle += ` ${eveNing}`; | |
textColor = `rgb(255 255 255 / ${isCurrentHour ? 1 : 0.6})`; | |
break; | |
case "tzName": | |
boxStyle += ` ${timezoneName}`; | |
break; | |
} | |
if (isCurrentHour) { | |
boxStyle += ` ${currentHour}`; | |
} | |
return ( | |
<div className={boxStyle} style={{ color: textColor }}> | |
{content} | |
</div> | |
); | |
}; | |
export const render = ({ times }) => { | |
if (times.length === 0) return <div>Loading...</div>; | |
const hoursBeforeCurrent = 0; // Show 11 hours before the current hour | |
return ( | |
<div className={container}> | |
<div className={widgetContent}> | |
<div className={rowsWrapper}> | |
{times.map((tz) => { | |
const watTimezone = (tz.name === "CET") ? currentTimezone : otherTimezone; | |
// const gradientOffset = ((24 - tz.hour) + 12) * 36; | |
const gradientOffset = tz.hour * 36; | |
// console.log(tz.name + " - " + tz.hour + " - " + gradientOffset); | |
return ( | |
<div key={tz.name} className={timezoneRow}> | |
<div className={hoursContainerWrapper}> | |
<div | |
className={hoursContainer + " " + watTimezone} | |
style={{ | |
backgroundImage: | |
`linear-gradient( | |
180deg in oklab, | |
hsl(240 50% 10%) 0%, | |
hsl(200 80% 33%) 15% 15%, 30%, | |
hsl(36 90% 50%) 50%, 70%, | |
hsl(200 80% 33%) 85% 85%, | |
hsl(240 50% 10%) 100% | |
)`, | |
backgroundPositionY: `${gradientOffset}px`, | |
backgroundRepeat: `repeat-y` | |
}} | |
> | |
{[...Array(24)].map((_, i) => { | |
const adjustedHour = | |
(tz.hour - hoursBeforeCurrent + i + 24) % 24; | |
// const hour12 = adjustedHour % 12 || 12; | |
// const ampm = adjustedHour < 12 ? "AM" : "PM"; | |
let type = "dayNight"; | |
if (adjustedHour >= 1 && adjustedHour <= 5) | |
type = "earlyMorning"; | |
else if (adjustedHour >= 6 && adjustedHour <= 7) | |
type = "morning"; | |
else if (adjustedHour >= 19 && adjustedHour <= 24) | |
type = "evening"; | |
if (adjustedHour === 0) { | |
return ( | |
<HourBox | |
key={i} | |
content={`${tz.month.toUpperCase()}\n${tz.day}`} | |
type="date" | |
isCurrentHour={false} | |
/> | |
); | |
} | |
return ( | |
<HourBox | |
key={i} | |
content={`${adjustedHour}`} | |
type={type} | |
isCurrentHour={i === hoursBeforeCurrent} | |
/> | |
); | |
})} | |
</div> | |
</div> | |
<HourBox content={`${tz.name}`} type="tzName"></HourBox> | |
</div> | |
); | |
})} | |
<div | |
className={currentHourConnector} | |
style={{ top: `${ ( ( hoursBeforeCurrent + 24 ) * 36) + 2}px`, width: `${timezones_count * 49}px` }} | |
/> | |
</div> | |
</div> | |
</div> | |
); | |
}; | |
const container = css` | |
:hover { | |
opacity: 1; | |
} | |
opacity: 1; | |
font-family: system-ui; | |
position: fixed; | |
width: fit-content; | |
height: fit-content; | |
// border-width: 1px 0 1px 1px; | |
// border-style: dotted; | |
// border-color: rgb(255 255 255 / 0.1); | |
// border-radius: 1rem 0 0 1rem; | |
border: 1px solid rgba(255, 255, 255, 0.1); | |
border-radius: 1rem; | |
top: -60px; | |
right: 40px; | |
padding: 1rem 2rem; | |
background-color: rgba(0, 0, 0, 0.2); | |
overflow: hidden; | |
box-shadow: -5px 5px 10px rgb(0 0 0 / .3); | |
transform: perspective(1500px) rotateX(40deg); | |
`; | |
const widgetContent = css` | |
display: flex; | |
justify-content: flex-start; | |
position: relative; | |
z-index: 1; | |
`; | |
const timezoneName = css` | |
color: hsl(0 0 100 / .5); | |
font-size: 16px; | |
font-weight: 300; | |
`; | |
const rowsWrapper = css` | |
display: flex; | |
flex-direction: row-reverse; | |
gap: 12px; | |
`; | |
const timezoneRow = css` | |
`; | |
const hoursContainerWrapper = css` | |
overflow: hidden; | |
// border: 1px solid rgba(255, 255, 255, 0.2); | |
`; | |
const hoursContainer = css` | |
display: flex; | |
flex-direction: column-reverse; | |
border-radius: 10px; | |
`; | |
const currentTimezone = css` | |
// border: 1px solid hsl(0 100 100 / 0.6); | |
`; | |
const otherTimezone = css` | |
opacity: 0.7; | |
`; | |
const hourBox = css` | |
width: 36px; | |
height: 36px; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
font-size: 16px; | |
flex-shrink: 0; | |
flex-direction: column; | |
line-height: 1; | |
`; | |
const earlyMorning = css` | |
// background-color: rgba(51, 51, 51, 0.1); | |
`; | |
const morning = css` | |
// background-color: rgba(102, 102, 102, 0.1); | |
`; | |
const dayNight = css` | |
// background-color: rgba(255, 255, 255, 0.1); | |
`; | |
const dateBox = css` | |
// background-color: rgba(51, 51, 51, 0.1); | |
width: 36px; | |
white-space: pre-line; | |
line-height: 1; | |
font-size: 11px !important; | |
text-align: center; | |
`; | |
const currentHour = css` | |
font-size: 18px; | |
font-weight: bold; | |
z-index: 4; | |
`; | |
const currentHourConnector = css` | |
position: absolute; | |
right: -10px; | |
border: 1px solid hsl(0 100 100 / 0.5); | |
border-radius: 5px; | |
height: 36px; | |
z-index: 10; | |
display:none; | |
`; |
Author
BorisAnthony
commented
Oct 9, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment