diff --git a/src/components/widgets/datetime/datetime.jsx b/src/components/widgets/datetime/datetime.jsx index fc883ec35..454d004dc 100644 --- a/src/components/widgets/datetime/datetime.jsx +++ b/src/components/widgets/datetime/datetime.jsx @@ -1,6 +1,8 @@ import { useState, useEffect } from "react"; import { useTranslation } from "next-i18next"; -import classNames from "classnames"; + +import Container from "../widget/container"; +import Raw from "../widget/raw"; const textSizes = { "4xl": "text-4xl", @@ -28,15 +30,14 @@ export default function DateTime({ options }) { }, [date, setDate, dateLocale, format]); return ( -
-
- - {date} - -
-
+ + +
+ + {date} + +
+
+
); } diff --git a/src/components/widgets/error.jsx b/src/components/widgets/error.jsx deleted file mode 100644 index 92e0076a1..000000000 --- a/src/components/widgets/error.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import { useTranslation } from "react-i18next"; -import { BiError } from "react-icons/bi"; -import classNames from "classnames"; - -export default function Error({ options }) { - const { t } = useTranslation(); - - return ( -
-
-
- -
- {t("widget.api_error")} -
-
-
-
- ); -} diff --git a/src/components/widgets/glances/glances.jsx b/src/components/widgets/glances/glances.jsx index b6daba7b7..debb09c73 100644 --- a/src/components/widgets/glances/glances.jsx +++ b/src/components/widgets/glances/glances.jsx @@ -3,10 +3,15 @@ import { useContext } from "react"; import { FaMemory, FaRegClock, FaThermometerHalf } from "react-icons/fa"; import { FiCpu, FiHardDrive } from "react-icons/fi"; import { useTranslation } from "next-i18next"; -import classNames from "classnames"; import UsageBar from "../resources/usage-bar"; -import Error from "../error"; +import Error from "../widget/error"; +import SingleResource from "../widget/single_resource"; +import WidgetIcon from "../widget/widget_icon"; +import ResourceValue from "../widget/resource_value"; +import ResourceLabel from "../widget/resource_label"; +import Resources from "../widget/resources"; +import WidgetLabel from "../widget/widget_label"; import { SettingsContext } from "utils/contexts/settings"; @@ -31,40 +36,33 @@ export default function Widget({ options }) { } if (!data) { - return ( -
-
-
- -
-
-
- {t("glances.wait")} -
-
- -
-
-
- -
-
-
- {t("glances.wait")} -
-
- -
-
-
- {options.label && ( -
{options.label}
- )} -
- ); + return + + + {t("glances.wait")} + + + + + {t("glances.wait")} + + + {options.cputemp && + + + {t("glances.wait")} + + + } + {options.uptime && + + + {t("glances.wait")} + + + } + {options.label && } + ; } const unit = options.units === "imperial" ? "fahrenheit" : "celsius"; @@ -94,134 +92,80 @@ export default function Widget({ options }) { } return ( - -
-
- -
-
-
- {t("common.number", { - value: data.cpu.total, - style: "unit", - unit: "percent", - maximumFractionDigits: 0, - })} -
-
{t("glances.cpu")}
-
- {options.expanded && ( - -
- {t("common.number", { - value: data.load.min15, - style: "unit", - unit: "percent", - maximumFractionDigits: 0, - })} -
-
{t("glances.load")}
-
- )} - -
-
-
- -
-
-
- {t("common.bytes", { - value: data.mem.free, - maximumFractionDigits: 1, - binary: true, - })} -
-
{t("glances.free")}
-
- {options.expanded && ( - -
- {t("common.bytes", { - value: data.mem.total, - maximumFractionDigits: 1, - binary: true, - })} -
-
{t("glances.total")}
-
- )} - -
-
- {disks.map((disk) => ( -
- -
- -
{t("common.bytes", { value: disk.free })}
-
{t("glances.free")}
-
- {options.expanded && ( - -
{t("common.bytes", { value: disk.size })}
-
{t("glances.total")}
-
- )} - -
-
))} - {options.cputemp && mainTemp > 0 && - (
- -
- -
- {t("common.number", { - value: mainTemp, - maximumFractionDigits: 1, - style: "unit", - unit - })} -
-
{t("glances.temp")}
-
- {options.expanded && ( - -
- {t("common.number", { - value: maxTemp, - maximumFractionDigits: 1, - style: "unit", - unit - })} -
-
{t("glances.warn")}
-
- )} - -
-
)} - {options.uptime && data.uptime && - (
- -
- -
- {data.uptime.replace(" days,", t("glances.days")).replace(/:\d\d:\d\d$/g, t("glances.hours"))} -
-
{t("glances.uptime")}
-
- -
-
)} -
- {options.label && ( -
{options.label}
- )} -
+ + + + {t("common.number", { + value: data.cpu.total, + style: "unit", + unit: "percent", + maximumFractionDigits: 0, + })} + {t("glances.cpu")} + {t("common.number", { + value: data.load.min15, + style: "unit", + unit: "percent", + maximumFractionDigits: 0, + })} + {t("glances.load")} + + + + + {t("common.bytes", { + value: data.mem.free, + maximumFractionDigits: 1, + binary: true, + })} + {t("glances.free")} + {t("common.bytes", { + value: data.mem.total, + maximumFractionDigits: 1, + binary: true, + })} + {t("glances.total")} + + + {disks.map((disk) => ( + + + {t("common.bytes", { value: disk.free })} + {t("glances.free")} + {t("common.bytes", { value: disk.size })} + {t("glances.total")} + + + ))} + {options.cputemp && mainTemp > 0 && + + + {t("common.number", { + value: mainTemp, + maximumFractionDigits: 1, + style: "unit", + unit + })} + {t("glances.temp")} + {t("common.number", { + value: maxTemp, + maximumFractionDigits: 1, + style: "unit", + unit + })} + {t("glances.warn")} + + + } + {options.uptime && data.uptime && + + + {data.uptime.replace(" days,", t("glances.days")).replace(/:\d\d:\d\d$/g, t("glances.hours"))} + {t("glances.uptime")} + + + } + {options.label && } + ); } diff --git a/src/components/widgets/greeting/greeting.jsx b/src/components/widgets/greeting/greeting.jsx index 2e129560c..11de571c8 100644 --- a/src/components/widgets/greeting/greeting.jsx +++ b/src/components/widgets/greeting/greeting.jsx @@ -1,4 +1,5 @@ -import classNames from "classnames"; +import Container from "../widget/container"; +import Raw from "../widget/raw"; const textSizes = { "4xl": "text-4xl", @@ -13,15 +14,12 @@ const textSizes = { export default function Greeting({ options }) { if (options.text) { - return ( -
+ return + {options.text} -
- ); + + ; } } diff --git a/src/components/widgets/kubernetes/kubernetes.jsx b/src/components/widgets/kubernetes/kubernetes.jsx index 514993da3..2d1f55e4b 100644 --- a/src/components/widgets/kubernetes/kubernetes.jsx +++ b/src/components/widgets/kubernetes/kubernetes.jsx @@ -1,8 +1,9 @@ import useSWR from "swr"; import { useTranslation } from "next-i18next"; -import classNames from "classnames"; -import Error from "../error"; +import Error from "../widget/error"; +import Container from "../widget/container"; +import Raw from "../widget/raw"; import Node from "./node"; @@ -20,7 +21,7 @@ export default function Widget({ options }) { used: 0, total: 0, free: 0, - precent: 0 + percent: 0 } }; @@ -35,11 +36,8 @@ export default function Widget({ options }) { } if (!data) { - return ( -
+ return +
{cluster.show && @@ -48,15 +46,12 @@ export default function Widget({ options }) { }
-
- ); + + ; } - return ( -
+ return +
{cluster.show && @@ -66,6 +61,6 @@ export default function Widget({ options }) { ) }
-
- ); + + ; } diff --git a/src/components/widgets/kubernetes/node.jsx b/src/components/widgets/kubernetes/node.jsx index 7a7c322d4..cc864be68 100644 --- a/src/components/widgets/kubernetes/node.jsx +++ b/src/components/widgets/kubernetes/node.jsx @@ -3,8 +3,7 @@ import { FiAlertTriangle, FiCpu, FiServer } from "react-icons/fi"; import { SiKubernetes } from "react-icons/si"; import { useTranslation } from "next-i18next"; -import UsageBar from "./usage-bar"; - +import UsageBar from "../resources/usage-bar"; export default function Node({ type, options, data }) { const { t } = useTranslation(); @@ -29,7 +28,7 @@ export default function Node({ type, options, data }) {
{t("common.number", { - value: data.cpu.percent, + value: data?.cpu?.percent ?? 0, style: "unit", unit: "percent", maximumFractionDigits: 0 @@ -37,18 +36,18 @@ export default function Node({ type, options, data }) {
- +
{t("common.bytes", { - value: data.memory.free, + value: data?.memory?.free ?? 0, maximumFractionDigits: 0, binary: true })}
- + {options.showLabel && (
{type === "cluster" ? options.label : data.name}
)} diff --git a/src/components/widgets/kubernetes/usage-bar.jsx b/src/components/widgets/kubernetes/usage-bar.jsx deleted file mode 100644 index c817db4c4..000000000 --- a/src/components/widgets/kubernetes/usage-bar.jsx +++ /dev/null @@ -1,12 +0,0 @@ -export default function UsageBar({ percent }) { - return ( -
-
-
- ); -} diff --git a/src/components/widgets/logo/logo.jsx b/src/components/widgets/logo/logo.jsx index 6cba17bf7..3a4a25651 100644 --- a/src/components/widgets/logo/logo.jsx +++ b/src/components/widgets/logo/logo.jsx @@ -1,14 +1,13 @@ -import classNames from "classnames"; +import Container from "../widget/container"; +import Raw from "../widget/raw"; import ResolvedIcon from "components/resolvedicon" export default function Logo({ options }) { return ( -
- {options.icon ? + + + {options.icon ? : // fallback to homepage logo } -
+ + ) } diff --git a/src/components/widgets/longhorn/longhorn.jsx b/src/components/widgets/longhorn/longhorn.jsx index 5139f00ad..c0169ceb9 100644 --- a/src/components/widgets/longhorn/longhorn.jsx +++ b/src/components/widgets/longhorn/longhorn.jsx @@ -1,7 +1,8 @@ import useSWR from "swr"; -import classNames from "classnames"; -import Error from "../error"; +import Error from "../widget/error"; +import Container from "../widget/container"; +import Raw from "../widget/raw"; import Node from "./node"; @@ -16,21 +17,15 @@ export default function Longhorn({ options }) { } if (!data) { - return ( -
+ return +
-
- ); +
+
; } - return ( -
+ return +
{data.nodes .filter((node) => { @@ -51,6 +46,6 @@ export default function Longhorn({ options }) {
)}
-
- ); + + ; } diff --git a/src/components/widgets/longhorn/node.jsx b/src/components/widgets/longhorn/node.jsx index e0ee69afb..9983486e3 100644 --- a/src/components/widgets/longhorn/node.jsx +++ b/src/components/widgets/longhorn/node.jsx @@ -1,32 +1,23 @@ -import { FiHardDrive } from "react-icons/fi"; import { useTranslation } from "next-i18next"; +import { FaThermometerHalf } from "react-icons/fa"; import UsageBar from "../resources/usage-bar"; +import SingleResource from "../widget/single_resource"; +import WidgetIcon from "../widget/widget_icon"; +import ResourceValue from "../widget/resource_value"; +import ResourceLabel from "../widget/resource_label"; +import WidgetLabel from "../widget/widget_label"; export default function Node({ data, expanded, labels }) { const { t } = useTranslation(); - return ( - <> -
- -
- -
{t("common.bytes", { value: data.node.available })}
-
{t("resources.free")}
-
- {expanded && ( - -
{t("common.bytes", { value: data.node.maximum })}
-
{t("resources.total")}
-
- )} - -
-
- {labels && ( -
{data.node.id}
- )} - - ); + return + + {t("common.bytes", { value: data.node.available })} + {t("resources.free")} + {t("common.bytes", { value: data.node.maximum })} + {t("resources.total")} + + { labels && } + } diff --git a/src/components/widgets/openmeteo/openmeteo.jsx b/src/components/widgets/openmeteo/openmeteo.jsx index 1381cc55a..040a3b6b1 100644 --- a/src/components/widgets/openmeteo/openmeteo.jsx +++ b/src/components/widgets/openmeteo/openmeteo.jsx @@ -3,9 +3,13 @@ import { useState } from "react"; import { WiCloudDown } from "react-icons/wi"; import { MdLocationDisabled, MdLocationSearching } from "react-icons/md"; import { useTranslation } from "next-i18next"; -import classNames from "classnames"; -import Error from "../error"; +import Error from "../widget/error"; +import Container from "../widget/container"; +import ContainerButton from "../widget/container_button"; +import WidgetIcon from "../widget/widget_icon"; +import PrimaryText from "../widget/primary_text"; +import SecondaryText from "../widget/secondary_text"; import Icon from "./icon"; @@ -21,50 +25,31 @@ function Widget({ options }) { } if (!data) { - return ( -
-
-
- -
-
- {t("weather.updating")} - {t("weather.wait")} -
-
-
- ); + return + {t("weather.updating")} + {t("weather.wait")} + + ; } const unit = options.units === "metric" ? "celsius" : "fahrenheit"; - const timeOfDay = data.current_weather.time > data.daily.sunrise[0] && data.current_weather.time < data.daily.sunset[0] ? "day" : "night"; + const weatherInfo = { + condition: data.current_weather.weathercode, + timeOfDay: data.current_weather.time > data.daily.sunrise[0] && data.current_weather.time < data.daily.sunset[0] ? "day" : "night" + }; - return ( -
-
-
- -
-
- - {options.label && `${options.label}, `} - {t("common.number", { - value: data.current_weather.temperature, - style: "unit", - unit, - })} - - {t(`wmo.${data.current_weather.weathercode}-${timeOfDay}`)} -
-
-
- ); + return + + {options.label && `${options.label}, `} + {t("common.number", { + value: data.current_weather.temperature, + style: "unit", + unit, + })} + + {t(`wmo.${data.current_weather.weathercode}-${weatherInfo.timeOfDay}`)} + + ; } export default function OpenMeteo({ options }) { @@ -99,29 +84,11 @@ export default function OpenMeteo({ options }) { // if (!requesting && !location) requestLocation(); if (!location) { - return ( - - ); + return + {t("weather.current")} + {t("weather.allow")} + + ; } return ; diff --git a/src/components/widgets/openweathermap/weather.jsx b/src/components/widgets/openweathermap/weather.jsx index b404039ff..305315139 100644 --- a/src/components/widgets/openweathermap/weather.jsx +++ b/src/components/widgets/openweathermap/weather.jsx @@ -3,12 +3,17 @@ import { useState } from "react"; import { WiCloudDown } from "react-icons/wi"; import { MdLocationDisabled, MdLocationSearching } from "react-icons/md"; import { useTranslation } from "next-i18next"; -import classNames from "classnames"; -import Error from "../error"; +import Error from "../widget/error"; +import Container from "../widget/container"; +import ContainerButton from "../widget/container_button"; +import PrimaryText from "../widget/primary_text"; +import SecondaryText from "../widget/secondary_text"; +import WidgetIcon from "../widget/widget_icon"; import Icon from "./icon"; + function Widget({ options }) { const { t, i18n } = useTranslation(); @@ -21,48 +26,26 @@ function Widget({ options }) { } if (!data) { - return ( -
-
-
- -
-
- {t("weather.updating")} - {t("weather.wait")} -
-
-
- ); + return + {t("weather.updating")} + {t("weather.wait")} + + ; } const unit = options.units === "metric" ? "celsius" : "fahrenheit"; - return ( -
-
-
- data.sys.sunrise && data.dt < data.sys.sunset ? "day" : "night"} - /> -
-
- - {options.label && `${options.label}, `} - {t("common.number", { value: data.main.temp, style: "unit", unit })} - - {data.weather[0].description} -
-
-
- ); + const weatherInfo = { + condition: data.weather[0].id, + timeOfDay: data.dt > data.sys.sunrise && data.dt < data.sys.sunset ? "day" : "night" + }; + + return + {options.label && `${options.label}, `} + {t("common.number", { value: data.main.temp, style: "unit", unit })} + {data.weather[0].description} + + ; } export default function OpenWeatherMap({ options }) { @@ -94,33 +77,12 @@ export default function OpenWeatherMap({ options }) { } }; - // if (!requesting && !location) requestLocation(); - if (!location) { - return ( - - ); + return + {t("weather.current")} + {t("weather.allow")} + + ; } return ; diff --git a/src/components/widgets/resources/cpu.jsx b/src/components/widgets/resources/cpu.jsx index 7069e3c4a..242e7a3db 100644 --- a/src/components/widgets/resources/cpu.jsx +++ b/src/components/widgets/resources/cpu.jsx @@ -1,8 +1,13 @@ import useSWR from "swr"; import { FiCpu } from "react-icons/fi"; -import { BiError } from "react-icons/bi"; import { useTranslation } from "next-i18next"; +import SingleResource from "../widget/single_resource"; +import WidgetIcon from "../widget/widget_icon"; +import ResourceValue from "../widget/resource_value"; +import ResourceLabel from "../widget/resource_label"; +import Error from "../widget/error"; + import UsageBar from "./usage-bar"; export default function Cpu({ expanded }) { @@ -13,67 +18,38 @@ export default function Cpu({ expanded }) { }); if (error || data?.error) { - return ( -
- -
- {t("widget.api_error")} -
-
- ); + return } if (!data) { - return ( -
- -
-
-
-
-
{t("resources.cpu")}
-
- {expanded && ( -
-
-
-
{t("resources.load")}
-
- )} - -
-
- ); + return + + - + {t("resources.cpu")} + - + {t("resources.load")} + + } - const percent = data.cpu.usage; - - return ( -
- -
-
-
- {t("common.number", { - value: data.cpu.usage, - style: "unit", - unit: "percent", - maximumFractionDigits: 0, - })} -
-
{t("resources.cpu")}
-
- {expanded && ( -
-
- {t("common.number", { - value: data.cpu.load, - maximumFractionDigits: 2, - })} -
-
{t("resources.load")}
-
- )} - -
-
- ); + return + + + {t("common.number", { + value: data.cpu.usage, + style: "unit", + unit: "percent", + maximumFractionDigits: 0, + })} + + {t("resources.cpu")} + + {t("common.number", { + value: data.cpu.load, + maximumFractionDigits: 2, + })} + + {t("resources.load")} + + } diff --git a/src/components/widgets/resources/cputemp.jsx b/src/components/widgets/resources/cputemp.jsx index 571e6c8a7..1a62aa31c 100644 --- a/src/components/widgets/resources/cputemp.jsx +++ b/src/components/widgets/resources/cputemp.jsx @@ -1,8 +1,13 @@ import useSWR from "swr"; import { FaThermometerHalf } from "react-icons/fa"; -import { BiError } from "react-icons/bi"; import { useTranslation } from "next-i18next"; +import SingleResource from "../widget/single_resource"; +import WidgetIcon from "../widget/widget_icon"; +import ResourceValue from "../widget/resource_value"; +import ResourceLabel from "../widget/resource_label"; +import Error from "../widget/error"; + import UsageBar from "./usage-bar"; function convertToFahrenheit(t) { @@ -17,34 +22,17 @@ export default function CpuTemp({ expanded, units }) { }); if (error || data?.error) { - return ( -
- -
- {t("widget.api_error")} -
-
- ); + return } if (!data || !data.cputemp) { - return ( -
- -
- -
-
-
{t("resources.temp")}
-
- {expanded && ( - -
-
-
{t("resources.max")}
-
- )} -
-
- ); + return + + - + {t("resources.temp")} + - + {t("resources.max")} + } let mainTemp = data.cputemp.main; @@ -54,38 +42,27 @@ export default function CpuTemp({ expanded, units }) { const unit = units === "imperial" ? "fahrenheit" : "celsius"; mainTemp = (unit === "celsius") ? mainTemp : convertToFahrenheit(mainTemp); const maxTemp = (unit === "celsius") ? data.cputemp.max : convertToFahrenheit(data.cputemp.max); - const percent = Math.round((mainTemp / maxTemp) * 100); - return ( -
- -
- -
- {t("common.number", { - value: mainTemp, - maximumFractionDigits: 1, - style: "unit", - unit - })} -
-
{t("resources.temp")}
-
- {expanded && ( - -
- {t("common.number", { - value: maxTemp, - maximumFractionDigits: 1, - style: "unit", - unit - })} -
-
{t("resources.max")}
-
- )} - -
-
- ); + return + + + {t("common.number", { + value: mainTemp, + maximumFractionDigits: 1, + style: "unit", + unit + })} + + {t("resources.temp")} + + {t("common.number", { + value: maxTemp, + maximumFractionDigits: 1, + style: "unit", + unit + })} + + {t("resources.max")} + + ; } diff --git a/src/components/widgets/resources/disk.jsx b/src/components/widgets/resources/disk.jsx index ca09c0958..742ff9d7d 100644 --- a/src/components/widgets/resources/disk.jsx +++ b/src/components/widgets/resources/disk.jsx @@ -1,8 +1,13 @@ import useSWR from "swr"; import { FiHardDrive } from "react-icons/fi"; -import { BiError } from "react-icons/bi"; import { useTranslation } from "next-i18next"; +import SingleResource from "../widget/single_resource"; +import WidgetIcon from "../widget/widget_icon"; +import ResourceValue from "../widget/resource_value"; +import ResourceLabel from "../widget/resource_label"; +import Error from "../widget/error"; + import UsageBar from "./usage-bar"; export default function Disk({ options, expanded }) { @@ -13,56 +18,29 @@ export default function Disk({ options, expanded }) { }); if (error || data?.error) { - return ( -
- -
- {t("widget.api_error")} -
-
- ); + return } if (!data) { - return ( -
- -
- -
-
-
{t("resources.free")}
-
- {expanded && ( - -
-
-
{t("resources.total")}
-
- )} - -
-
- ); + return + + - + {t("resources.free")} + - + {t("resources.total")} + + ; } // data.drive.used not accurate? const percent = Math.round(((data.drive.size - data.drive.available) / data.drive.size) * 100); - return ( -
- -
- -
{t("common.bytes", { value: data.drive.available })}
-
{t("resources.free")}
-
- {expanded && ( - -
{t("common.bytes", { value: data.drive.size })}
-
{t("resources.total")}
-
- )} - -
-
- ); + return + + {t("common.bytes", { value: data.drive.available })} + {t("resources.free")} + {t("common.bytes", { value: data.drive.size })} + {t("resources.total")} + + ; } diff --git a/src/components/widgets/resources/memory.jsx b/src/components/widgets/resources/memory.jsx index 30b7c8eb8..97c74acce 100644 --- a/src/components/widgets/resources/memory.jsx +++ b/src/components/widgets/resources/memory.jsx @@ -1,8 +1,13 @@ import useSWR from "swr"; import { FaMemory } from "react-icons/fa"; -import { BiError } from "react-icons/bi"; import { useTranslation } from "next-i18next"; +import SingleResource from "../widget/single_resource"; +import WidgetIcon from "../widget/widget_icon"; +import ResourceValue from "../widget/resource_value"; +import ResourceLabel from "../widget/resource_label"; +import Error from "../widget/error"; + import UsageBar from "./usage-bar"; export default function Memory({ expanded }) { @@ -13,63 +18,34 @@ export default function Memory({ expanded }) { }); if (error || data?.error) { - return ( -
- -
- {t("widget.api_error")} -
-
- ); + return } if (!data) { - return ( -
- -
- -
-
-
{t("resources.free")}
-
- {expanded && ( - -
-
-
{t("resources.total")}
-
- )} - -
-
- ); + return + + - + {t("resources.free")} + - + {t("resources.total")} + + ; } const percent = Math.round((data.memory.active / data.memory.total) * 100); - return ( -
- -
- -
- {t("common.bytes", { value: data.memory.available, maximumFractionDigits: 1, binary: true })} -
-
{t("resources.free")}
-
- {expanded && ( - -
- {t("common.bytes", { - value: data.memory.total, - maximumFractionDigits: 1, - binary: true, - })} -
-
{t("resources.total")}
-
- )} - -
-
- ); + return + + {t("common.bytes", { value: data.memory.available, maximumFractionDigits: 1, binary: true })} + {t("resources.free")} + + {t("common.bytes", { + value: data.memory.total, + maximumFractionDigits: 1, + binary: true, + })} + + {t("resources.total")} + + ; } diff --git a/src/components/widgets/resources/resources.jsx b/src/components/widgets/resources/resources.jsx index 5727a2a00..0cc2c3013 100644 --- a/src/components/widgets/resources/resources.jsx +++ b/src/components/widgets/resources/resources.jsx @@ -1,4 +1,5 @@ -import classNames from "classnames"; +import Container from "../widget/container"; +import Raw from "../widget/raw"; import Disk from "./disk"; import Cpu from "./cpu"; @@ -8,11 +9,8 @@ import Uptime from "./uptime"; export default function Resources({ options }) { const { expanded, units } = options; - return ( -
+ return +
{options.cpu && } {options.memory && } @@ -25,6 +23,6 @@ export default function Resources({ options }) { {options.label && (
{options.label}
)} -
- ); +
+
; } diff --git a/src/components/widgets/resources/uptime.jsx b/src/components/widgets/resources/uptime.jsx index 3bf785b1e..6cc2b8c5e 100644 --- a/src/components/widgets/resources/uptime.jsx +++ b/src/components/widgets/resources/uptime.jsx @@ -1,8 +1,13 @@ import useSWR from "swr"; import { FaRegClock } from "react-icons/fa"; -import { BiError } from "react-icons/bi"; import { useTranslation } from "next-i18next"; +import SingleResource from "../widget/single_resource"; +import WidgetIcon from "../widget/widget_icon"; +import ResourceValue from "../widget/resource_value"; +import ResourceLabel from "../widget/resource_label"; +import Error from "../widget/error"; + import UsageBar from "./usage-bar"; export default function Uptime() { @@ -13,35 +18,22 @@ export default function Uptime() { }); if (error || data?.error) { - return ( -
- -
- {t("widget.api_error")} -
-
- ); + return } if (!data) { - return ( -
- -
- -
-
-
{t("resources.temp")}
-
-
-
- ); + return + + - + {t("resources.uptime")} + ; } const mo = Math.floor(data.uptime / (3600 * 24 * 31)); const d = Math.floor(data.uptime % (3600 * 24 * 31) / (3600 * 24)); const h = Math.floor(data.uptime % (3600 * 24) / 3600); const m = Math.floor(data.uptime % 3600 / 60); - + let uptime; if (mo > 0) uptime = `${mo}${t("resources.months")} ${d}${t("resources.days")}`; else if (d > 0) uptime = `${d}${t("resources.days")} ${h}${t("resources.hours")}`; @@ -49,18 +41,10 @@ export default function Uptime() { const percent = Math.round((new Date().getSeconds() / 60) * 100); - return ( -
- -
- -
- {uptime} -
-
{t("resources.uptime")}
-
- -
-
- ); + return + + {uptime} + {t("resources.uptime")} + + ; } diff --git a/src/components/widgets/search/search.jsx b/src/components/widgets/search/search.jsx index bca3eb58b..1bac4a619 100644 --- a/src/components/widgets/search/search.jsx +++ b/src/components/widgets/search/search.jsx @@ -1,10 +1,13 @@ -import { useState, useEffect, Fragment } from "react"; +import { useState, useEffect, useCallback, Fragment } from "react"; import { useTranslation } from "next-i18next"; import { FiSearch } from "react-icons/fi"; import { SiDuckduckgo, SiMicrosoftbing, SiGoogle, SiBaidu, SiBrave } from "react-icons/si"; import { Listbox, Transition } from "@headlessui/react"; import classNames from "classnames"; +import ContainerForm from "../widget/container_form"; +import Raw from "../widget/raw"; + export const searchProviders = { google: { name: "Google", @@ -77,13 +80,8 @@ export default function Search({ options }) { } }, [availableProviderIds]); - if (!availableProviderIds) { - return null; - } - - function handleSubmit(event) { + const submitCallback = useCallback(event => { const q = encodeURIComponent(query); - const { url } = selectedProvider; if (url) { window.open(`${url}${q}`, options.target || "_blank"); @@ -94,6 +92,10 @@ export default function Search({ options }) { event.preventDefault(); event.target.reset(); setQuery(""); + }, [options.target, options.url, query, selectedProvider]); + + if (!availableProviderIds) { + return null; } const onChangeProvider = (provider) => { @@ -101,80 +103,79 @@ export default function Search({ options }) { localStorage.setItem(localStorageKey, provider.name); } - return ( -
-
- setQuery(s.currentTarget.value)} - required - autoCapitalize="off" - autoCorrect="off" - autoComplete="off" - // eslint-disable-next-line jsx-a11y/no-autofocus - autoFocus={options.focus} - /> - -
- - - {t("search.search")} - -
- - + +
+
+ setQuery(s.currentTarget.value)} + required + autoCapitalize="off" + autoCorrect="off" + autoComplete="off" + // eslint-disable-next-line jsx-a11y/no-autofocus + autoFocus={options.focus} + /> + +
+ + + {t("search.search")} + +
+ -
- {availableProviderIds.map((providerId) => { - const p = searchProviders[providerId]; - return ( - - {({ active }) => ( -
  • - -
  • - )} -
    - ); - })} -
    - -
    -
    - - ); + +
    + {availableProviderIds.map((providerId) => { + const p = searchProviders[providerId]; + return ( + + {({ active }) => ( +
  • + +
  • + )} +
    + ); + })} +
    +
    + + +
    + + ; } diff --git a/src/components/widgets/unifi_console/unifi_console.jsx b/src/components/widgets/unifi_console/unifi_console.jsx index 1896771f8..dad92cc78 100644 --- a/src/components/widgets/unifi_console/unifi_console.jsx +++ b/src/components/widgets/unifi_console/unifi_console.jsx @@ -2,9 +2,12 @@ import { BiError, BiWifi, BiCheckCircle, BiXCircle, BiNetworkChart } from "react import { MdSettingsEthernet } from "react-icons/md"; import { useTranslation } from "next-i18next"; import { SiUbiquiti } from "react-icons/si"; -import classNames from "classnames"; -import Error from "../error"; +import Error from "../widget/error"; +import Container from "../widget/container"; +import Raw from "../widget/raw"; +import WidgetIcon from "../widget/widget_icon"; +import PrimaryText from "../widget/primary_text"; import useWidgetAPI from "utils/proxy/use-widget-api"; @@ -22,21 +25,10 @@ export default function Widget({ options }) { const defaultSite = options.site ? statsData?.data.find(s => s.desc === options.site) : statsData?.data?.find(s => s.name === "default"); if (!defaultSite) { - return ( -
    -
    -
    - -
    -
    - {t("unifi.wait")} -
    -
    -
    - ); + return + {t("unifi.wait")} + + ; } const wan = defaultSite.health.find(h => h.subsystem === "wan"); @@ -51,11 +43,9 @@ export default function Widget({ options }) { const dataEmpty = !(wan.show || lan.show || wlan.show || uptime); - return ( -
    + return + +
    @@ -139,6 +129,7 @@ export default function Widget({ options }) {
    }
    -
    - ); +
    +
    + } diff --git a/src/components/widgets/weather/weather.jsx b/src/components/widgets/weather/weather.jsx index 518014552..702ea6693 100644 --- a/src/components/widgets/weather/weather.jsx +++ b/src/components/widgets/weather/weather.jsx @@ -3,9 +3,13 @@ import { useState } from "react"; import { WiCloudDown } from "react-icons/wi"; import { MdLocationDisabled, MdLocationSearching } from "react-icons/md"; import { useTranslation } from "next-i18next"; -import classNames from "classnames"; -import Error from "../error"; +import Error from "../widget/error"; +import Container from "../widget/container"; +import PrimaryText from "../widget/primary_text"; +import SecondaryText from "../widget/secondary_text"; +import WidgetIcon from "../widget/widget_icon"; +import ContainerButton from "../widget/container_button"; import Icon from "./icon"; @@ -21,49 +25,31 @@ function Widget({ options }) { } if (!data) { - return ( -
    -
    -
    - -
    -
    - {t("weather.updating")} - {t("weather.wait")} -
    -
    -
    - ); + return + {t("weather.updating")} + {t("weather.wait")} + + ; } const unit = options.units === "metric" ? "celsius" : "fahrenheit"; + const weatherInfo = { + condition: data.current.condition.code, + timeOfDay: data.current.is_day ? "day" : "night", + }; - return ( -
    -
    -
    - -
    -
    - - {options.label && `${options.label}, `} - {t("common.number", { - value: options.units === "metric" ? data.current.temp_c : data.current.temp_f, - style: "unit", - unit, - })} - - {data.current.condition.text} -
    -
    -
    - ); + return + + {options.label && `${options.label}, `} + {t("common.number", { + value: options.units === "metric" ? data.current.temp_c : data.current.temp_f, + style: "unit", + unit, + })} + + {data.current.condition.text} + + ; } export default function WeatherApi({ options }) { @@ -95,33 +81,12 @@ export default function WeatherApi({ options }) { } }; - // if (!requesting && !location) requestLocation(); - if (!location) { - return ( - - ); + return + {t("weather.current")} + {t("weather.allow")} + + ; } return ; diff --git a/src/components/widgets/widget.jsx b/src/components/widgets/widget.jsx index 471418872..b4fdb1434 100644 --- a/src/components/widgets/widget.jsx +++ b/src/components/widgets/widget.jsx @@ -17,13 +17,13 @@ const widgetMappings = { kubernetes: dynamic(() => import("components/widgets/kubernetes/kubernetes")), }; -export default function Widget({ widget }) { +export default function Widget({ widget, style }) { const InfoWidget = widgetMappings[widget.type]; if (InfoWidget) { return ( - + ); } diff --git a/src/components/widgets/widget/container.jsx b/src/components/widgets/widget/container.jsx new file mode 100644 index 000000000..3a4a9f57e --- /dev/null +++ b/src/components/widgets/widget/container.jsx @@ -0,0 +1,42 @@ +import classNames from "classnames"; + +import WidgetIcon from "./widget_icon"; +import PrimaryText from "./primary_text"; +import SecondaryText from "./secondary_text"; +import Raw from "./raw"; + +export function getAllClasses(options, additionalClassNames = '') { + return classNames( + "flex flex-col justify-center first:ml-0 ml-4 mr-2", + additionalClassNames, + options?.style === "boxedWidgets" && " ml-4 mt-2 m:mb-0 rounded-md shadow-md shadow-theme-900/10 dark:shadow-theme-900/20 bg-theme-100/20 dark:bg-white/5 p-2 pl-3", + ); +} + +export function getInnerBlock(children) { + // children won't be an array if it's Raw component + return Array.isArray(children) &&
    +
    {children.find(child => child.type === WidgetIcon)}
    +
    + {children.find(child => child.type === PrimaryText)} + {children.find(child => child.type === SecondaryText)} +
    +
    ; +} + +export function getBottomBlock(children) { + if (children.type !== Raw) { + return children.find(child => child.type === Raw) || []; + } + + return [children]; +} + +export default function Container({ children = [], options, additionalClassNames = '' }) { + return ( +
    + {getInnerBlock(children)} + {getBottomBlock(children)} +
    + ); +} diff --git a/src/components/widgets/widget/container_button.jsx b/src/components/widgets/widget/container_button.jsx new file mode 100644 index 000000000..92d8a4166 --- /dev/null +++ b/src/components/widgets/widget/container_button.jsx @@ -0,0 +1,10 @@ +import { getAllClasses, getInnerBlock, getBottomBlock } from "./container"; + +export default function ContainerButton ({ children = [], options, additionalClassNames = '', callback }) { + return ( + + ); +} diff --git a/src/components/widgets/widget/container_form.jsx b/src/components/widgets/widget/container_form.jsx new file mode 100644 index 000000000..7d28a1bb3 --- /dev/null +++ b/src/components/widgets/widget/container_form.jsx @@ -0,0 +1,10 @@ +import { getAllClasses, getInnerBlock, getBottomBlock } from "./container"; + +export default function ContainerForm ({ children = [], options, additionalClassNames = '', callback }) { + return ( +
    + {getInnerBlock(children)} + {getBottomBlock(children)} +
    + ); +} diff --git a/src/components/widgets/widget/container_link.jsx b/src/components/widgets/widget/container_link.jsx new file mode 100644 index 000000000..8ef0e80aa --- /dev/null +++ b/src/components/widgets/widget/container_link.jsx @@ -0,0 +1,10 @@ +import { getAllClasses, getInnerBlock, getBottomBlock } from "./container"; + +export default function ContainerLink ({ children = [], options, additionalClassNames = '', target }) { + return ( + + {getInnerBlock(children)} + {getBottomBlock(children)} + + ); +} diff --git a/src/components/widgets/widget/error.jsx b/src/components/widgets/widget/error.jsx new file mode 100644 index 000000000..a3dbab85e --- /dev/null +++ b/src/components/widgets/widget/error.jsx @@ -0,0 +1,15 @@ +import { useTranslation } from "react-i18next"; +import { BiError } from "react-icons/bi"; + +import Container from "./container"; +import PrimaryText from "./primary_text"; +import WidgetIcon from "./widget_icon"; + +export default function Error({ options }) { + const { t } = useTranslation(); + + return + {t("widget.api_error")} + + ; +} diff --git a/src/components/widgets/widget/primary_text.jsx b/src/components/widgets/widget/primary_text.jsx new file mode 100644 index 000000000..3418b92c7 --- /dev/null +++ b/src/components/widgets/widget/primary_text.jsx @@ -0,0 +1,5 @@ +export default function PrimaryText({ children }) { + return ( + {children} + ); +} diff --git a/src/components/widgets/widget/raw.jsx b/src/components/widgets/widget/raw.jsx new file mode 100644 index 000000000..44e3dddc4 --- /dev/null +++ b/src/components/widgets/widget/raw.jsx @@ -0,0 +1,7 @@ +export default function Raw({ children }) { + if (children.type === Raw) { + return [children]; + } + + return children; +} diff --git a/src/components/widgets/widget/resource_label.jsx b/src/components/widgets/widget/resource_label.jsx new file mode 100644 index 000000000..87f2ad22b --- /dev/null +++ b/src/components/widgets/widget/resource_label.jsx @@ -0,0 +1,5 @@ +export default function ResourceLabel({ children }) { + return ( +
    {children}
    + ); +} diff --git a/src/components/widgets/widget/resource_value.jsx b/src/components/widgets/widget/resource_value.jsx new file mode 100644 index 000000000..8971c748d --- /dev/null +++ b/src/components/widgets/widget/resource_value.jsx @@ -0,0 +1,5 @@ +export default function ResourceValue({ children }) { + return ( +
    {children}
    + ); +} diff --git a/src/components/widgets/widget/resources.jsx b/src/components/widgets/widget/resources.jsx new file mode 100644 index 000000000..0771ec5e8 --- /dev/null +++ b/src/components/widgets/widget/resources.jsx @@ -0,0 +1,15 @@ +import ContainerLink from "./container_link"; +import SingleResource from "./single_resource"; +import Raw from "./raw"; +import WidgetLabel from "./widget_label"; + +export default function Resources({ options, children, target }) { + return + +
    + {children.filter(child => child && child.type === SingleResource)} +
    + {children.filter(child => child && child.type === WidgetLabel)} +
    +
    ; +} diff --git a/src/components/widgets/widget/secondary_text.jsx b/src/components/widgets/widget/secondary_text.jsx new file mode 100644 index 000000000..363d1bd04 --- /dev/null +++ b/src/components/widgets/widget/secondary_text.jsx @@ -0,0 +1,5 @@ +export default function SecondaryText({ children }) { + return ( + {children} + ); +} diff --git a/src/components/widgets/widget/single_resource.jsx b/src/components/widgets/widget/single_resource.jsx new file mode 100644 index 000000000..7a83d8bed --- /dev/null +++ b/src/components/widgets/widget/single_resource.jsx @@ -0,0 +1,28 @@ +import UsageBar from "../resources/usage-bar"; + +import WidgetIcon from "./widget_icon"; +import ResourceValue from "./resource_value"; +import ResourceLabel from "./resource_label"; +import Raw from "./raw"; + +export default function SingleResource({ children, key, expanded = false }) { + const values = children.filter(child => child.type === ResourceValue); + const labels = children.filter(child => child.type === ResourceLabel); + + return
    + {children.find(child => child.type === WidgetIcon)} +
    +
    + {values.pop()} + {labels.pop()} +
    + { expanded &&
    + {values.pop()} + {labels.pop()} +
    + } + {children.find(child => child.type === UsageBar)} +
    + {children.find(child => child.type === Raw)} +
    ; +} diff --git a/src/components/widgets/widget/widget_icon.jsx b/src/components/widgets/widget/widget_icon.jsx new file mode 100644 index 000000000..9766a8791 --- /dev/null +++ b/src/components/widgets/widget/widget_icon.jsx @@ -0,0 +1,18 @@ +export default function WidgetIcon({ icon, size = "s", pulse = false, weatherInfo = {} }) { + const Icon = icon; + const { condition, timeOfDay } = weatherInfo; + let additionalClasses = "text-theme-800 dark:text-theme-200 "; + + switch (size) { + case "m": additionalClasses += "w-6 h-6 "; break; + case "l": additionalClasses += "w-8 h-8 "; break; + case "xl": additionalClasses += "w-10 h-10 "; break; + default: additionalClasses += "w-5 h-5 "; + } + + if (pulse) { + additionalClasses += "animate-pulse "; + } + + return ; +} diff --git a/src/components/widgets/widget/widget_label.jsx b/src/components/widgets/widget/widget_label.jsx new file mode 100644 index 000000000..dcb9b9e96 --- /dev/null +++ b/src/components/widgets/widget/widget_label.jsx @@ -0,0 +1,3 @@ +export default function WidgetLabel({ label = "" }) { + return
    {label}
    +} diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 6180ff51e..7611aed54 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -160,6 +160,7 @@ const headerStyles = { "m-4 mb-0 sm:m-8 sm:mb-0 rounded-md shadow-md shadow-theme-900/10 dark:shadow-theme-900/20 bg-theme-100/20 dark:bg-white/5 p-3", underlined: "m-4 mb-0 sm:m-8 sm:mb-1 border-b-2 pb-4 border-theme-800 dark:border-theme-200/50", clean: "m-4 mb-0 sm:m-8 sm:mb-0", + boxedWidgets: "m-4 mb-0 sm:m-8 sm:mb-0 sm:mt-1", }; function Home({ initialSettings }) { @@ -208,6 +209,7 @@ function Home({ initialSettings }) { searchProvider = searchProviders[searchWidget.options?.provider]; } } + const headerStyle = initialSettings?.headerStyle || "underlined"; useEffect(() => { function handleKeyDown(e) { @@ -256,7 +258,7 @@ function Home({ initialSettings }) {
    !rightAlignedWidgets.includes(widget.type)) .map((widget, i) => ( - + ))} -
    +
    {widgets .filter((widget) => rightAlignedWidgets.includes(widget.type)) .map((widget, i) => ( - + ))}