feat: add world clock module (#795)

Co-authored-by: Louis Dalibard <ontake@ontake.dev>
Co-authored-by: Jas Singh <jaskiratpal.singh@outlook.com>
This commit is contained in:
Louis Dalibard
2025-04-05 08:10:51 +02:00
committed by GitHub
parent 9a40475f4a
commit 1d717e9f2e
9 changed files with 291 additions and 0 deletions

View File

@@ -25,6 +25,7 @@ import { Power } from '../../components/bar/modules/power/index';
import { Hyprsunset } from '../../components/bar/modules/hyprsunset/index';
import { Hypridle } from '../../components/bar/modules/hypridle/index';
import { Cava } from '../../components/bar/modules/cava/index';
import { WorldClock } from '../../components/bar/modules/worldclock/index';
export {
Menu,
@@ -54,4 +55,5 @@ export {
Hyprsunset,
Hypridle,
Cava,
WorldClock,
};

View File

@@ -26,6 +26,7 @@ import {
Hyprsunset,
Hypridle,
Cava,
WorldClock,
} from './exports';
import { WidgetContainer } from './shared/WidgetContainer';
@@ -67,6 +68,7 @@ const widget = {
hyprsunset: (): JSX.Element => WidgetContainer(Hyprsunset()),
hypridle: (): JSX.Element => WidgetContainer(Hypridle()),
cava: (): JSX.Element => WidgetContainer(Cava()),
worldclock: (): JSX.Element => WidgetContainer(WorldClock()),
};
const gdkMonitorMapper = new GdkMonitorMapper();

View File

@@ -0,0 +1,120 @@
import { openMenu } from '../../utils/menu';
import options from 'src/options';
import { BarBoxChild } from 'src/lib/types/bar.js';
import { runAsyncCommand, throttledScrollHandler } from 'src/components/bar/utils/helpers.js';
import { bind, Variable } from 'astal';
import { onMiddleClick, onPrimaryClick, onScroll, onSecondaryClick } from 'src/lib/shared/eventHandlers';
import { Astal } from 'astal/gtk3';
import { systemTime } from 'src/globals/time';
import { GLib } from 'astal';
const { format, formatDiffDate, tz, icon, showIcon, showTime, rightClick, middleClick, scrollUp, scrollDown } =
options.bar.customModules.worldclock;
const { style } = options.theme.bar.buttons;
const time = Variable.derive(
[systemTime, format, formatDiffDate, tz],
(c, f, fdd, tzn) =>
tzn
.map((t) =>
c
.to_timezone(GLib.TimeZone.new(t))
.format(c.to_timezone(GLib.TimeZone.new(t)).get_day_of_year() == c.get_day_of_year() ? f : fdd),
)
.join(' | ') || '',
);
const WorldClock = (): BarBoxChild => {
const ClockTime = (): JSX.Element => <label className={'bar-button-label worldclock bar'} label={bind(time)} />;
const ClockIcon = (): JSX.Element => (
<label className={'bar-button-icon worldclock txt-icon bar'} label={bind(icon)} />
);
const componentClassName = Variable.derive(
[bind(style), bind(showIcon), bind(showTime)],
(btnStyle, shwIcn, shwLbl) => {
const styleMap = {
default: 'style1',
split: 'style2',
wave: 'style3',
wave2: 'style3',
};
return `worldclock-container ${styleMap[btnStyle]} ${!shwLbl ? 'no-label' : ''} ${!shwIcn ? 'no-icon' : ''}`;
},
);
const componentChildren = Variable.derive([bind(showIcon), bind(showTime)], (shIcn, shTm) => {
if (shIcn && !shTm) {
return <ClockIcon />;
} else if (shTm && !shIcn) {
return <ClockTime />;
}
return (
<box>
<ClockIcon />
<ClockTime />
</box>
);
});
const component = (
<box
className={componentClassName()}
onDestroy={() => {
componentClassName.drop();
componentChildren.drop();
}}
>
{componentChildren()}
</box>
);
return {
component,
isVisible: true,
boxClass: 'worldclock',
props: {
setup: (self: Astal.Button): void => {
let disconnectFunctions: (() => void)[] = [];
Variable.derive(
[
bind(rightClick),
bind(middleClick),
bind(scrollUp),
bind(scrollDown),
bind(options.bar.scrollSpeed),
],
() => {
disconnectFunctions.forEach((disconnect) => disconnect());
disconnectFunctions = [];
const throttledHandler = throttledScrollHandler(options.bar.scrollSpeed.get());
disconnectFunctions.push(
onPrimaryClick(self, (clicked, event) => {
openMenu(clicked, event, 'calendarmenu');
}),
);
disconnectFunctions.push(
onSecondaryClick(self, (clicked, event) => {
runAsyncCommand(rightClick.get(), { clicked, event });
}),
);
disconnectFunctions.push(
onMiddleClick(self, (clicked, event) => {
runAsyncCommand(middleClick.get(), { clicked, event });
}),
);
disconnectFunctions.push(onScroll(self, throttledHandler, scrollUp.get(), scrollDown.get()));
},
);
},
},
};
};
export { WorldClock };

View File

@@ -419,6 +419,29 @@ export const CustomModuleSettings = (): JSX.Element => {
<Option opt={options.bar.customModules.cava.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.cava.scrollDown} title="Scroll Down" type="string" />
{/* World Clock Section */}
<Header title="World Clock" />
<Option
opt={options.theme.bar.buttons.modules.worldclock.enableBorder}
title="Button Border"
type="boolean"
/>
<Option opt={options.bar.customModules.worldclock.icon} title="Icon" type="string" />
<Option opt={options.bar.customModules.worldclock.showIcon} title="Show Icon" type="boolean" />
<Option opt={options.theme.bar.buttons.modules.worldclock.spacing} title="Spacing" type="string" />
<Option opt={options.bar.customModules.worldclock.showTime} title="Show Time" type="boolean" />
<Option opt={options.bar.customModules.worldclock.format} title="Format" type="string" />
<Option
opt={options.bar.customModules.worldclock.formatDiffDate}
title="Format (when date different from local date)"
type="string"
/>
<Option opt={options.bar.customModules.worldclock.rightClick} title="Right Click" type="string" />
<Option opt={options.bar.customModules.worldclock.middleClick} title="Middle Click" type="string" />
<Option opt={options.bar.customModules.worldclock.scrollUp} title="Scroll Up" type="string" />
<Option opt={options.bar.customModules.worldclock.scrollDown} title="Scroll Down" type="string" />
<Option opt={options.bar.customModules.worldclock.tz} title="Timezones Displayed" type="object" />
{/* Power Section */}
<Header title="Power" />
<Option

View File

@@ -223,6 +223,23 @@ export const CustomModuleTheme = (): JSX.Element => {
/>
<Option opt={options.theme.bar.buttons.modules.cava.border} title="Border" type="color" />
{/* World Clock Module Section */}
<Header title="World Clock" />
<Option opt={options.theme.bar.buttons.modules.worldclock.text} title="Bars" type="color" />
<Option opt={options.theme.bar.buttons.modules.worldclock.icon} title="Icon" type="color" />
<Option
opt={options.theme.bar.buttons.modules.worldclock.background}
title="Label Background"
type="color"
/>
<Option
opt={options.theme.bar.buttons.modules.worldclock.icon_background}
title="Icon Background"
subtitle="Applies a background color to the icon section of the button.\nRequires 'split' button styling."
type="color"
/>
<Option opt={options.theme.bar.buttons.modules.worldclock.border} title="Border" type="color" />
{/* Power Module Section */}
<Header title="Power" />
<Option opt={options.theme.bar.buttons.modules.power.icon} title="Icon" type="color" />

View File

@@ -435,6 +435,15 @@ const options = mkOptions({
icon_background: opt(colors.base2),
spacing: opt('0.5em'),
},
worldclock: {
enableBorder: opt(false),
border: opt(colors.yellow),
background: opt(colors.base2),
text: opt(colors.yellow),
icon: opt(colors.yellow),
icon_background: opt(colors.base2),
spacing: opt('0.5em'),
},
},
},
menus: {
@@ -1235,6 +1244,18 @@ const options = mkOptions({
scrollUp: opt(''),
scrollDown: opt(''),
},
worldclock: {
icon: opt('󱉊'),
showIcon: opt(true),
showTime: opt(true),
format: opt('%I:%M:%S %p %Z'),
formatDiffDate: opt('%a %b %d %I:%M:%S %p %Z'),
rightClick: opt(''),
middleClick: opt(''),
scrollUp: opt(''),
scrollDown: opt(''),
tz: opt(['America/New_York', 'Europe/Paris', 'Asia/Tokyo']),
},
},
},

View File

@@ -58,3 +58,69 @@
0em
);
}
.bar-button-label.worldclock {
color: if($bar-buttons-monochrome, $bar-buttons-text, $bar-buttons-modules-worldclock-text);
margin-left: $bar-buttons-modules-worldclock-spacing;
}
.bar-button-icon.worldclock {
font-size: 1.2em;
color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-modules-worldclock-icon);
}
.style2 {
.bar-button-icon.worldclock {
border-top-left-radius: $bar-buttons-radius;
border-bottom-left-radius: $bar-buttons-radius;
background: if(
$bar-buttons-monochrome,
$bar-buttons-icon_background,
$bar-buttons-modules-worldclock-icon_background
);
padding: $bar-buttons-padding_y 0em;
padding-left: $bar-buttons-padding_x;
padding-right: $bar-buttons-modules-worldclock-spacing;
border-top-left-radius: if(
$bar-buttons-modules-worldclock-enableBorder or $bar-buttons-enableBorders,
$bar-buttons-radius * $bar-buttons-innerRadiusMultiplier,
$bar-buttons-radius
);
border-bottom-left-radius: if(
$bar-buttons-modules-worldclock-enableBorder or $bar-buttons-enableBorders,
$bar-buttons-radius * $bar-buttons-innerRadiusMultiplier,
$bar-buttons-radius
);
color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-modules-worldclock-icon);
}
.bar-button-label.worldclock {
padding: $bar-buttons-padding_y 0em;
padding-right: $bar-buttons-padding_x;
padding-left: $bar-buttons-modules-worldclock-spacing;
margin-left: 0em;
}
&.no-label.worldclock-container {
.bar-button-icon.worldclock {
border-top-right-radius: if(
$bar-buttons-modules-worldclock-enableBorder or $bar-buttons-enableBorders,
$bar-buttons-radius * $bar-buttons-innerRadiusMultiplier,
$bar-buttons-radius
);
border-bottom-right-radius: if(
$bar-buttons-modules-worldclock-enableBorder or $bar-buttons-enableBorders,
$bar-buttons-radius * $bar-buttons-innerRadiusMultiplier,
$bar-buttons-radius
);
}
}
}
.bar_item_box_visible.worldclock {
border: if(
$bar-buttons-modules-worldclock-enableBorder or $bar-buttons-enableBorders,
$bar-buttons-borderSize solid
if($bar-buttons-monochrome, $bar-buttons-borderColor, $bar-buttons-modules-worldclock-border),
0em
);
}

View File

@@ -503,3 +503,30 @@
// custom font size
1.2em //
);
/*
* #################################
* # World Clock Styling #
* #################################
*/
@include styleModule(
//
// class name
'worldclock',
// label color
$bar-buttons-modules-worldclock-text,
// icon color
$bar-buttons-modules-worldclock-icon,
// icon background if split style is used
$bar-buttons-modules-worldclock-icon_background,
// label background
$bar-buttons-modules-worldclock-background,
// inner spacing
$bar-buttons-modules-worldclock-spacing,
// if border enabled
$bar-buttons-modules-worldclock-enableBorder,
// border color
$bar-buttons-modules-worldclock-border,
// custom font size
1.2em //
);