nixos/modules/home/ags/app/widget/ToggleButton.tsx

115 lines
3.3 KiB
TypeScript

import GLib from "gi://GLib"
import Pango from "gi://Pango"
import Gtk from "gi://Gtk?version=3.0"
import { bind, type Binding, type Variable } from "astal"
// rotating arrow button: icon turns 90deg in small steps while the section
// opens, ported from AlfheimOS; a single self-clearing chained timer drives
// the animation so cancellation only ever touches provably live sources
export function Arrow({
open,
onToggle,
}: {
open: Variable<boolean>
onToggle: () => void
}) {
let deg = 0
let timer: number | undefined
const icon = <icon icon="pan-end-symbolic" />
const animate = (target: number) => {
if (timer !== undefined) {
GLib.source_remove(timer)
timer = undefined
}
const tick = () => {
deg += Math.sign(target - deg) * 10
icon.css = `-gtk-icon-transform: rotate(${deg}deg);`
if (deg !== target) {
timer = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 15, tick)
} else {
timer = undefined
}
return GLib.SOURCE_REMOVE
}
if (deg !== target) tick()
}
open.subscribe((v) => animate(v ? 90 : 0))
return (
<button className="arrow" onClicked={onToggle}>
{icon}
</button>
)
}
type ArrowToggleButtonProps = {
open: Variable<boolean>
icon: string | Binding<string>
label: string | Binding<string>
active: Binding<boolean>
onMainClick: () => void
onArrowClick: () => void
}
// icon + label pill with a detached arrow: the pill toggles the feature,
// the arrow rotates open its device menu
export function ArrowToggleButton({
open,
icon,
label,
active,
onMainClick,
onArrowClick,
}: ArrowToggleButtonProps) {
return (
<box
className={active.as((v) => (v ? "toggle-button active" : "toggle-button"))}
hexpand
>
<button className="toggle-pill" hexpand onClicked={onMainClick}>
<box hexpand>
<icon className="icon" icon={icon} />
<label
className="label"
label={label}
maxWidthChars={10}
ellipsize={Pango.EllipsizeMode.END}
/>
</box>
</button>
<Arrow open={open} onToggle={onArrowClick} />
</box>
)
}
type MenuProps = {
open: Variable<boolean>
icon: string | Binding<string>
title: string
children: Array<JSX.Element>
}
// revealer with a titled card header, shown while the section is open
export function Menu({ open, icon, title, children }: MenuProps) {
return (
<revealer
transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}
transitionDuration={180}
revealChild={bind(open)}
>
<box className="menu" vertical>
<box className="title-box">
<icon className="icon" icon={icon} />
<label className="title" label={title} ellipsize={Pango.EllipsizeMode.END} />
</box>
<box className="menu-separator" />
<box className="content" vertical>
{children}
</box>
</box>
</revealer>
)
}