From 0046e5dd82cc215e6403f579a507db874bfae238 Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Wed, 31 Jul 2024 15:13:53 +0200 Subject: [PATCH] feat(waybar): implement all dummy widgets --- src/App.tsx | 38 +++++++-- src/components/Waybar/Waybar.tsx | 51 ------------ src/components/Waybar/WaybarWidget.tsx | 9 ++- src/components/Waybar/WaybarWidgetGroup.tsx | 10 ++- .../Waybar/Widgets/WaybarBatteryWidget.tsx | 27 +++++++ .../Waybar/Widgets/WaybarBrightnessWidget.tsx | 26 +++++++ .../Waybar/Widgets/WaybarCPUWidget.tsx | 16 ++-- .../Waybar/Widgets/WaybarDiskWidget.tsx | 2 +- .../Waybar/Widgets/WaybarHomeWidget.tsx | 5 ++ .../Waybar/Widgets/WaybarLockWidget.tsx | 9 +++ .../Waybar/Widgets/WaybarMicrophoneWidget.tsx | 33 ++++++++ .../Waybar/Widgets/WaybarPowerWidget.tsx | 9 +++ .../Waybar/Widgets/WaybarRAMWidget.tsx | 2 +- .../Waybar/Widgets/WaybarSessionWidget.tsx | 9 --- .../Widgets/WaybarTemperatureWidget.tsx | 29 +++++++ .../Waybar/Widgets/WaybarTimeWidget.tsx | 24 ++++++ .../Waybar/Widgets/WaybarTitleWidget.tsx | 4 +- .../Widgets/WaybarToggleThemeWidget.tsx | 7 ++ .../Waybar/Widgets/WaybarTrayWidget.tsx | 5 ++ .../Waybar/Widgets/WaybarVolumeWidget.tsx | 37 +++++++++ .../Waybar/Widgets/WaybarWeatherWidget.tsx | 5 ++ src/components/Waybar/index.tsx | 78 +++++++++++++++++++ src/context/AppContext.tsx | 10 ++- src/providers/AppProvider.tsx | 14 +++- src/utils/icons.ts | 3 + src/utils/math.ts | 5 ++ src/utils/react.ts | 4 + src/utils/types.ts | 9 +++ 28 files changed, 396 insertions(+), 84 deletions(-) delete mode 100644 src/components/Waybar/Waybar.tsx create mode 100644 src/components/Waybar/Widgets/WaybarBatteryWidget.tsx create mode 100644 src/components/Waybar/Widgets/WaybarBrightnessWidget.tsx create mode 100644 src/components/Waybar/Widgets/WaybarHomeWidget.tsx create mode 100644 src/components/Waybar/Widgets/WaybarLockWidget.tsx create mode 100644 src/components/Waybar/Widgets/WaybarMicrophoneWidget.tsx create mode 100644 src/components/Waybar/Widgets/WaybarPowerWidget.tsx delete mode 100644 src/components/Waybar/Widgets/WaybarSessionWidget.tsx create mode 100644 src/components/Waybar/Widgets/WaybarTimeWidget.tsx create mode 100644 src/components/Waybar/Widgets/WaybarToggleThemeWidget.tsx create mode 100644 src/components/Waybar/Widgets/WaybarTrayWidget.tsx create mode 100644 src/components/Waybar/Widgets/WaybarVolumeWidget.tsx create mode 100644 src/components/Waybar/Widgets/WaybarWeatherWidget.tsx create mode 100644 src/components/Waybar/index.tsx create mode 100644 src/utils/math.ts create mode 100644 src/utils/react.ts diff --git a/src/App.tsx b/src/App.tsx index 8282ba2..bdc3f46 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,20 +3,36 @@ import { Kitty } from "./components/Kitty"; import { AppProvider } from "./providers/AppProvider"; import { Music } from "./components/Music"; import { Nvim } from "./components/Nvim"; +import { Waybar } from "./components/Waybar"; +import { useApp } from "./hooks/useApp"; +import { clamp } from "./utils/math"; -export default function App() { +const AppRoot = () => { const [loggedIn, setLoggedIn] = useState(false); + const { brightness } = useApp(); + + const opacity = clamp(0.5 - (0.5 * brightness) / 100, 0, 0.5); return ( - + <> +
{loggedIn ? ( -
- - - +
+
+ +
- +
+ + + + + +
) : (
@@ -29,6 +45,14 @@ export default function App() {
)}
+ + ); +}; + +export default function App() { + return ( + + ); } diff --git a/src/components/Waybar/Waybar.tsx b/src/components/Waybar/Waybar.tsx deleted file mode 100644 index 0d3b7b2..0000000 --- a/src/components/Waybar/Waybar.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { WaybarWidgetGroup } from "./WaybarWidgetGroup"; -import { WaybarCPUWidget } from "./Widgets/WaybarCPUWidget"; -import { WaybarDiskWidget } from "./Widgets/WaybarDiskWidget"; -import { WaybarRAMWidget } from "./Widgets/WaybarRAMWidget"; -import { WaybarTitleWidget } from "./Widgets/WaybarTitleWidget"; - -export const Waybar = () => { - return ( -
- - - - - - - - - - {/* - - - - - - - - - - - - - - - - - - - - - - */} -
- ); -}; diff --git a/src/components/Waybar/WaybarWidget.tsx b/src/components/Waybar/WaybarWidget.tsx index 9dedbf9..ca8712d 100644 --- a/src/components/Waybar/WaybarWidget.tsx +++ b/src/components/Waybar/WaybarWidget.tsx @@ -4,9 +4,16 @@ import { cn } from "~/utils/react"; export const WaybarWidget = (props: { className?: string; tooltip?: string; + interactable?: boolean; children: ReactNode | Array; }) => ( -
+
{props.children}
); diff --git a/src/components/Waybar/WaybarWidgetGroup.tsx b/src/components/Waybar/WaybarWidgetGroup.tsx index f70965b..bb71bce 100644 --- a/src/components/Waybar/WaybarWidgetGroup.tsx +++ b/src/components/Waybar/WaybarWidgetGroup.tsx @@ -1,9 +1,17 @@ import { type ReactNode } from "react"; +import { cn } from "~/utils/react"; export const WaybarWidgetGroup = (props: { + className?: string; children: ReactNode | Array; }) => ( -
+
{props.children}
); +// gap-5 px-3 py-[6.5px] diff --git a/src/components/Waybar/Widgets/WaybarBatteryWidget.tsx b/src/components/Waybar/Widgets/WaybarBatteryWidget.tsx new file mode 100644 index 0000000..4ceaf86 --- /dev/null +++ b/src/components/Waybar/Widgets/WaybarBatteryWidget.tsx @@ -0,0 +1,27 @@ +import { useEffect, useState } from "react"; +import { WaybarWidget } from "../WaybarWidget"; +import { lerpIcon } from "~/utils/icons"; + +const ICONS = ["󰂎", "󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"]; + +export const WaybarBatteryWidget = (props: { frequency: number }) => { + const [battery, setBattery] = useState(100); + + useEffect(() => { + const interval = setInterval(() => { + setBattery((x) => x - 1); + + if (battery - 1 === 0) { + // TODO: do something + } + }, props.frequency); + + return () => clearInterval(interval); + }); + + return ( + + {lerpIcon(ICONS, battery, 100)} {battery}% + + ); +}; diff --git a/src/components/Waybar/Widgets/WaybarBrightnessWidget.tsx b/src/components/Waybar/Widgets/WaybarBrightnessWidget.tsx new file mode 100644 index 0000000..8a20584 --- /dev/null +++ b/src/components/Waybar/Widgets/WaybarBrightnessWidget.tsx @@ -0,0 +1,26 @@ +import { type WheelEvent } from "react"; +import { clamp } from "~/utils/math"; +import { WaybarWidget } from "../WaybarWidget"; +import { useApp } from "~/hooks/useApp"; +import { lerpIcon } from "~/utils/icons"; + +const ICONS = ["󰃞", "󰃟", "󰃠"]; + +export const WaybarBrightnessWidget = () => { + const { brightness, setBrightness } = useApp(); + + const handleScroll = (e: WheelEvent) => { + let newBrightness = brightness - Math.sign(e.deltaY); + newBrightness = clamp(newBrightness, 0, 100); + + setBrightness(newBrightness); + }; + + return ( + + + {lerpIcon(ICONS, brightness, 100)} {brightness}% + + + ); +}; diff --git a/src/components/Waybar/Widgets/WaybarCPUWidget.tsx b/src/components/Waybar/Widgets/WaybarCPUWidget.tsx index 568e188..32f3ab4 100644 --- a/src/components/Waybar/Widgets/WaybarCPUWidget.tsx +++ b/src/components/Waybar/Widgets/WaybarCPUWidget.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useState } from "react"; import { WaybarWidget } from "../WaybarWidget"; // 1 is really active, but often changes, 19-30% @@ -11,10 +11,6 @@ export const WaybarCPUWidget = (props: { frequency: number; }) => { const [usage, setUsage] = useState(new Array(props.cores).fill(0)); - const totalUsage = useMemo( - () => Math.round(usage.reduce((acc, v) => acc + v, 0) / usage.length), - [usage], - ); useEffect(() => { const interval = setInterval(() => { @@ -23,7 +19,13 @@ export const WaybarCPUWidget = (props: { }, props.frequency); return () => clearInterval(interval); - }, [usage]); + }, [usage, props.frequency]); - return  {totalUsage}%; + const totalUsage = Math.round( + usage.reduce((acc, v) => acc + v, 0) / usage.length, + ); + + return ( +  {totalUsage}% + ); }; diff --git a/src/components/Waybar/Widgets/WaybarDiskWidget.tsx b/src/components/Waybar/Widgets/WaybarDiskWidget.tsx index c091781..2d8040d 100644 --- a/src/components/Waybar/Widgets/WaybarDiskWidget.tsx +++ b/src/components/Waybar/Widgets/WaybarDiskWidget.tsx @@ -12,5 +12,5 @@ export const WaybarDiskWidget = (props: { props.current + randomMinMax(-props.variation, props.variation + 1); const usage = Math.round((value / props.capacity) * 100); - return 󰋊 {usage}%; + return 󰋊 {usage}%; }; diff --git a/src/components/Waybar/Widgets/WaybarHomeWidget.tsx b/src/components/Waybar/Widgets/WaybarHomeWidget.tsx new file mode 100644 index 0000000..7d6e8c8 --- /dev/null +++ b/src/components/Waybar/Widgets/WaybarHomeWidget.tsx @@ -0,0 +1,5 @@ +import { WaybarWidget } from "../WaybarWidget"; + +export const WaybarHomeWidget = () => ( + +); diff --git a/src/components/Waybar/Widgets/WaybarLockWidget.tsx b/src/components/Waybar/Widgets/WaybarLockWidget.tsx new file mode 100644 index 0000000..783ce15 --- /dev/null +++ b/src/components/Waybar/Widgets/WaybarLockWidget.tsx @@ -0,0 +1,9 @@ +import { WaybarWidget } from "../WaybarWidget"; + +export const WaybarLockWidget = () => { + return ( + +  + + ); +}; diff --git a/src/components/Waybar/Widgets/WaybarMicrophoneWidget.tsx b/src/components/Waybar/Widgets/WaybarMicrophoneWidget.tsx new file mode 100644 index 0000000..a36603d --- /dev/null +++ b/src/components/Waybar/Widgets/WaybarMicrophoneWidget.tsx @@ -0,0 +1,33 @@ +import { type WheelEvent, useState } from "react"; +import { clamp } from "~/utils/math"; +import { WaybarWidget } from "../WaybarWidget"; + +export const WaybarMicrophoneWidget = () => { + const [muted, setMuted] = useState(false); + const [volume, setVolume] = useState(100); + + const handleWheel = (e: WheelEvent) => { + let newVolume = volume - Math.sign(e.deltaY) * 5; + newVolume = clamp(newVolume, 0, 100); + + setVolume(newVolume); + }; + + const handleClick = () => { + setMuted((muted) => !muted); + }; + + const icon = muted ? "" : ""; + + return ( + + + {icon} {!muted && `${volume}%`} + + + ); +}; diff --git a/src/components/Waybar/Widgets/WaybarPowerWidget.tsx b/src/components/Waybar/Widgets/WaybarPowerWidget.tsx new file mode 100644 index 0000000..dd3e314 --- /dev/null +++ b/src/components/Waybar/Widgets/WaybarPowerWidget.tsx @@ -0,0 +1,9 @@ +import { WaybarWidget } from "../WaybarWidget"; + +export const WaybarPowerWidget = () => { + return ( + +  + + ); +}; diff --git a/src/components/Waybar/Widgets/WaybarRAMWidget.tsx b/src/components/Waybar/Widgets/WaybarRAMWidget.tsx index 4d4e4f8..77a0659 100644 --- a/src/components/Waybar/Widgets/WaybarRAMWidget.tsx +++ b/src/components/Waybar/Widgets/WaybarRAMWidget.tsx @@ -26,5 +26,5 @@ export const WaybarRAMWidget = (props: { // TODO: tooltip // Memory - (capacity * usage).1f GB used - return  {usage}%; + return  {usage}%; }; diff --git a/src/components/Waybar/Widgets/WaybarSessionWidget.tsx b/src/components/Waybar/Widgets/WaybarSessionWidget.tsx deleted file mode 100644 index b6eb377..0000000 --- a/src/components/Waybar/Widgets/WaybarSessionWidget.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { WaybarWidget } from "./WaybarWidget"; - -export const WaybarSessionWidget = () => ( - - - 03:13 PM - - -); diff --git a/src/components/Waybar/Widgets/WaybarTemperatureWidget.tsx b/src/components/Waybar/Widgets/WaybarTemperatureWidget.tsx index e69de29..f1f7dba 100644 --- a/src/components/Waybar/Widgets/WaybarTemperatureWidget.tsx +++ b/src/components/Waybar/Widgets/WaybarTemperatureWidget.tsx @@ -0,0 +1,29 @@ +import { useEffect, useState } from "react"; +import { WaybarWidget } from "../WaybarWidget"; +import { clamp, randomMinMax } from "~/utils/math"; + +export const WaybarTemperatureWidget = (props: { + min: number; + max: number; + variation: number; + frequency: number; +}) => { + const [temperature, setTemperature] = useState( + randomMinMax(props.min, props.max), + ); + + useEffect(() => { + const interval = setInterval(() => { + const offset = randomMinMax(-props.variation, props.variation + 1); + setTemperature((x) => clamp(x + offset, props.min, props.max)); + }, props.frequency); + + return () => clearInterval(interval); + }); + + return ( + +  {temperature}°C + + ); +}; diff --git a/src/components/Waybar/Widgets/WaybarTimeWidget.tsx b/src/components/Waybar/Widgets/WaybarTimeWidget.tsx new file mode 100644 index 0000000..0f309f9 --- /dev/null +++ b/src/components/Waybar/Widgets/WaybarTimeWidget.tsx @@ -0,0 +1,24 @@ +import { useEffect, useState } from "react"; +import { WaybarWidget } from "../WaybarWidget"; + +export const WaybarTimeWidget = () => { + const [date, setDate] = useState(new Date()); + + useEffect(() => { + const interval = setInterval(() => { + setDate(new Date()); + }, 1000); + + return () => { + clearInterval(interval); + }; + }); + + const time = date.toLocaleString("en-US", { + hour: "2-digit", + minute: "2-digit", + hour12: true, + }); + + return {time}; +}; diff --git a/src/components/Waybar/Widgets/WaybarTitleWidget.tsx b/src/components/Waybar/Widgets/WaybarTitleWidget.tsx index 1ad44be..6ab25dd 100644 --- a/src/components/Waybar/Widgets/WaybarTitleWidget.tsx +++ b/src/components/Waybar/Widgets/WaybarTitleWidget.tsx @@ -1,3 +1,5 @@ import { WaybarWidget } from "../WaybarWidget"; -export const WaybarTitleWidget = () => nvim; +export const WaybarTitleWidget = () => ( + nvim +); diff --git a/src/components/Waybar/Widgets/WaybarToggleThemeWidget.tsx b/src/components/Waybar/Widgets/WaybarToggleThemeWidget.tsx new file mode 100644 index 0000000..9fe042e --- /dev/null +++ b/src/components/Waybar/Widgets/WaybarToggleThemeWidget.tsx @@ -0,0 +1,7 @@ +import { WaybarWidget } from "../WaybarWidget"; + +export const WaybarToggleThemeWidget = () => ( + + 󰐾 + +); diff --git a/src/components/Waybar/Widgets/WaybarTrayWidget.tsx b/src/components/Waybar/Widgets/WaybarTrayWidget.tsx new file mode 100644 index 0000000..2b47af7 --- /dev/null +++ b/src/components/Waybar/Widgets/WaybarTrayWidget.tsx @@ -0,0 +1,5 @@ +import { WaybarWidget } from "../WaybarWidget"; + +export const WaybarTrayWidget = () => ( + X +); diff --git a/src/components/Waybar/Widgets/WaybarVolumeWidget.tsx b/src/components/Waybar/Widgets/WaybarVolumeWidget.tsx new file mode 100644 index 0000000..95c1c18 --- /dev/null +++ b/src/components/Waybar/Widgets/WaybarVolumeWidget.tsx @@ -0,0 +1,37 @@ +import { type WheelEvent, useState } from "react"; +import { clamp } from "~/utils/math"; +import { WaybarWidget } from "../WaybarWidget"; +import { useApp } from "~/hooks/useApp"; +import { lerpIcon } from "~/utils/icons"; + +const ICONS = ["", "", ""]; + +export const WaybarVolumeWidget = () => { + const [muted, setMuted] = useState(false); + const { volume, setVolume } = useApp(); + + const handleWheel = (e: WheelEvent) => { + let newVolume = volume - Math.sign(e.deltaY) * 5; + newVolume = clamp(newVolume, 0, 100); + + setVolume(newVolume); + }; + + const handleClick = () => { + setMuted(!muted); + }; + + const icon = muted ? "" : lerpIcon(ICONS, volume, 100); + + return ( + + + {icon} {!muted && `${volume}%`} + + + ); +}; diff --git a/src/components/Waybar/Widgets/WaybarWeatherWidget.tsx b/src/components/Waybar/Widgets/WaybarWeatherWidget.tsx new file mode 100644 index 0000000..51b8484 --- /dev/null +++ b/src/components/Waybar/Widgets/WaybarWeatherWidget.tsx @@ -0,0 +1,5 @@ +import { WaybarWidget } from "../WaybarWidget"; + +export const WaybarWeatherWidget = () => ( + 🌧️ 27° +); diff --git a/src/components/Waybar/index.tsx b/src/components/Waybar/index.tsx new file mode 100644 index 0000000..df6def0 --- /dev/null +++ b/src/components/Waybar/index.tsx @@ -0,0 +1,78 @@ +import { WaybarWidgetGroup } from "./WaybarWidgetGroup"; +import { WaybarCPUWidget } from "./Widgets/WaybarCPUWidget"; +import { WaybarDiskWidget } from "./Widgets/WaybarDiskWidget"; +import { WaybarRAMWidget } from "./Widgets/WaybarRAMWidget"; +import { WaybarTitleWidget } from "./Widgets/WaybarTitleWidget"; +import { WaybarHomeWidget } from "./Widgets/WaybarHomeWidget"; +import { randomMinMax } from "~/utils/math"; +import { WaybarTemperatureWidget } from "./Widgets/WaybarTemperatureWidget"; +import { WaybarBatteryWidget } from "./Widgets/WaybarBatteryWidget"; +import { WaybarBrightnessWidget } from "./Widgets/WaybarBrightnessWidget"; +import { WaybarVolumeWidget } from "./Widgets/WaybarVolumeWidget"; +import { WaybarMicrophoneWidget } from "./Widgets/WaybarMicrophoneWidget"; +import { WaybarLockWidget } from "./Widgets/WaybarLockWidget"; +import { WaybarTimeWidget } from "./Widgets/WaybarTimeWidget"; +import { WaybarPowerWidget } from "./Widgets/WaybarPowerWidget"; +import { WaybarTrayWidget } from "./Widgets/WaybarTrayWidget"; +import { WaybarToggleThemeWidget } from "./Widgets/WaybarToggleThemeWidget"; +import { WaybarWeatherWidget } from "./Widgets/WaybarWeatherWidget"; + +export const Waybar = () => { + return ( +
+
+ + + + + + + + + + + + + +
+
+ + + + + +
+
+ + + + + + + + + + + + + + + + + +
+
+ ); +}; diff --git a/src/context/AppContext.tsx b/src/context/AppContext.tsx index 84f8c51..a8768b7 100644 --- a/src/context/AppContext.tsx +++ b/src/context/AppContext.tsx @@ -1,9 +1,11 @@ import { createContext } from "react"; +import { type Prettify, type State } from "~/utils/types"; export const AppContext = createContext< - | { - activeKitty: string; - setActiveKitty: (value: string) => void; - } + | Prettify< + State<"activeKitty", string> & + State<"brightness", number> & + State<"volume", number> + > | undefined >(undefined); diff --git a/src/providers/AppProvider.tsx b/src/providers/AppProvider.tsx index b9cc068..68bc1c2 100644 --- a/src/providers/AppProvider.tsx +++ b/src/providers/AppProvider.tsx @@ -3,8 +3,20 @@ import { AppContext } from "~/context/AppContext"; export const AppProvider = (props: { children?: ReactNode }) => { const [activeKitty, setActiveKitty] = useState(":r0:"); + const [brightness, setBrightness] = useState(100); + const [volume, setVolume] = useState(100); + return ( - + {props.children} ); diff --git a/src/utils/icons.ts b/src/utils/icons.ts index 39b1b9d..fdb1eea 100644 --- a/src/utils/icons.ts +++ b/src/utils/icons.ts @@ -1,5 +1,8 @@ import { type Child, type Icon } from "./tree"; +export const lerpIcon = (icons: Array, value: number, max: number) => + icons[Math.floor((value / max) * icons.length)] ?? icons[icons.length - 1]; + export const getIcon = (file: Child | string | undefined): Icon => { if (!file) return DEFAULT_ICON; diff --git a/src/utils/math.ts b/src/utils/math.ts new file mode 100644 index 0000000..44cbfc0 --- /dev/null +++ b/src/utils/math.ts @@ -0,0 +1,5 @@ +export const clamp = (v: number, min: number, max: number) => + Math.min(Math.max(v, min), max); + +export const randomMinMax = (min: number, max: number) => + Math.floor(Math.random() * (max - min) + min); diff --git a/src/utils/react.ts b/src/utils/react.ts new file mode 100644 index 0000000..441e078 --- /dev/null +++ b/src/utils/react.ts @@ -0,0 +1,4 @@ +import { type ClassNameValue, twMerge } from "tailwind-merge"; +import { clsx } from "clsx"; + +export const cn = (...classes: Array) => twMerge(clsx(classes)); diff --git a/src/utils/types.ts b/src/utils/types.ts index 92455d8..7536160 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -6,3 +6,12 @@ export type Prettify = NonNullable<{ [K in keyof T]: T[K] }>; export type InnerKittyProps any> = Prettify< Parameters[0] & KittyContextProps >; + +export type State< + Name extends string, + T, +> = Name extends `${infer First}${infer Rest}` + ? { + [K in Name]: T; + } & { [K in `set${Capitalize}${Rest}`]: (value: T) => void } + : never;