diff --git a/apps/docs/.screenshots/home-integration-dataflow.png b/apps/docs/.screenshots/home-integration-dataflow.png new file mode 100644 index 000000000..d39cf5d6e Binary files /dev/null and b/apps/docs/.screenshots/home-integration-dataflow.png differ diff --git a/apps/docs/src/components/pages/home/dataflow/dataflow.tsx b/apps/docs/src/components/pages/home/dataflow/dataflow.tsx index 0c04cb279..aad23fef9 100644 --- a/apps/docs/src/components/pages/home/dataflow/dataflow.tsx +++ b/apps/docs/src/components/pages/home/dataflow/dataflow.tsx @@ -1,147 +1,84 @@ -import React, { useRef } from "react"; +import styles from "../../../../pages/index.module.css"; +import { supportedIntegrations } from "../../../../constants/supported-integrations"; -import LeaderLine, { SocketType } from "leader-line-new"; +const nodePositions = [ + styles.dataflowNodeTopLeft, + styles.dataflowNodeMiddleLeft, + styles.dataflowNodeBottomLeft, + styles.dataflowNodeTopRight, + styles.dataflowNodeMiddleRight, + styles.dataflowNodeBottomRight, +]; +const cyclingIntegrations = supportedIntegrations.filter((integration) => integration.name !== "Homarr"); -export const DataflowVisualizationComponent = () => { - const homarrRef = useRef(null); - const sonarrRef = useRef(null); - const lidarrRef = useRef(null); - const radarrRef = useRef(null); - const jellyfinRef = useRef(null); - const plexRef = useRef(null); - const sabnzbdRef = useRef(null); +const paths = [ + "M145 82 C330 82 330 235 480 260", + "M145 260 C300 260 360 260 480 260", + "M145 438 C330 438 330 285 480 260", + "M480 260 C630 235 630 82 815 82", + "M480 260 C600 260 660 260 815 260", + "M480 260 C630 285 630 438 815 438", +]; - return ( -
-

+export const DataflowVisualizationComponent = () => ( +
+
+

No YAML configurations.
Easy and quick to manage integrations.

+

Homarr brings status, controls, and updates together in one dashboard.

+
-
- {"Homarr +
+ - {"Radarr"} - {"Sonarr"} - {"Lidarr"} + {nodePositions.map((position, nodeIndex) => { + const cycleStepInSeconds = 1.2 + nodeIndex * 0.13; + const phaseOffsetInSeconds = nodeIndex * 2.7; - {"Jellyfin"} + return ( +
+ +
+ ); + })} - {"Sabnzbd"} - - - - - - - +
+
- ); -}; - -export const LineTree = ({ - start, - end, - startSocket, - endSocket, - x = 0, -}: { - start: any; - end: any; - startSocket: SocketType; - endSocket: SocketType; - x?: number; -}) => { - const line = useRef(null); - let leaderLine: LeaderLine; - - React.useEffect(() => { - const drawLine = () => { - leaderLine = new LeaderLine(start.current, LeaderLine.pointAnchor(end.current, { x: `${x}%` }), { - startSocket: startSocket, - endSocket: endSocket, - color: "var(--ifm-color-primary)", - size: 3, - dash: { - animation: true, - }, - }); - }; - const timer = setInterval(() => { - if (start.current) { - clearInterval(timer); - drawLine(); - } - }, 5); - return () => { - timer && clearInterval(timer); - leaderLine.remove(); - }; - }, []); - - React.useEffect(() => { - // scroll and resize listeners could be assigned here - setTimeout(() => { - // skip current even loop and wait - // the end of parent's render call - if (line.current && end?.current) { - (line.current as any).position(); - } - }, 0); - }); - - return null; -}; +
+); diff --git a/apps/docs/src/pages/index.module.css b/apps/docs/src/pages/index.module.css index 36af84e40..302bc94c0 100644 --- a/apps/docs/src/pages/index.module.css +++ b/apps/docs/src/pages/index.module.css @@ -230,6 +230,172 @@ section { font-weight: 700; } +.dataflowSection { + margin-bottom: 0; + padding: clamp(4rem, 8vw, 7rem) clamp(1rem, 4vw, 3rem); + overflow: hidden; + background: rgb(0 0 0 / 10%); +} + +.dataflowHeading { + max-width: 58rem; + margin: 0 auto clamp(2rem, 5vw, 4rem); + text-align: center; +} + +.dataflowHeading h2 { + margin-bottom: 1rem; + font-size: clamp(2rem, 5vw, 4rem); + line-height: 1.08; + letter-spacing: -0.03em; + text-wrap: balance; +} + +.dataflowHeading p { + margin: 0 auto; + max-width: 42rem; + color: color-mix(in srgb, var(--ifm-font-color-base) 76%, transparent); + font-size: clamp(1rem, 2vw, 1.25rem); + text-wrap: pretty; +} + +.dataflowDiagram { + position: relative; + width: min(100%, 70rem); + aspect-ratio: 960 / 520; + margin-inline: auto; +} + +.dataflowLines { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + overflow: visible; +} + +.dataflowLine, +.dataflowSignal, +.dataflowSignalReverse { + fill: none; + stroke-linecap: round; +} + +.dataflowLine { + stroke: color-mix(in srgb, var(--ifm-color-primary) 30%, transparent); + stroke-width: 3; +} + +.dataflowSignal, +.dataflowSignalReverse { + stroke: var(--ifm-color-primary); + stroke-width: 7; + stroke-dasharray: 0.035 0.965; + animation: dataflow-signal 2.8s linear infinite; +} + +.dataflowSignalReverse { + animation-direction: reverse; + animation-delay: -1.4s; +} + +.dataflowNode { + position: absolute; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.5rem; + box-sizing: border-box; + width: clamp(4.5rem, 10vw, 7.5rem); + aspect-ratio: 1; + padding: clamp(0.6rem, 1.5vw, 1rem); + border-radius: 0.875rem; + color: var(--ifm-font-color-base); + background: var(--ifm-background-surface-color); + box-shadow: 0 6px 12px rgb(31 41 55 / 14%); + transform: translate(-50%, -50%); +} + +.dataflowNode img { + width: clamp(2rem, 4.5vw, 3.4rem); + height: clamp(2rem, 4.5vw, 3.4rem); + object-fit: contain; +} + +.dataflowCyclingIcons { + position: relative; + width: clamp(2rem, 4.5vw, 3.4rem); + aspect-ratio: 1; +} + +.dataflowCyclingIcons img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + opacity: 0; + animation: dataflow-icon-cycle linear infinite; +} + +.dataflowNodeTopLeft, +.dataflowNodeMiddleLeft, +.dataflowNodeBottomLeft { + left: 15%; +} + +.dataflowNodeTopRight, +.dataflowNodeMiddleRight, +.dataflowNodeBottomRight { + left: 85%; +} + +.dataflowNodeTopLeft, +.dataflowNodeTopRight { + top: 16%; +} + +.dataflowNodeMiddleLeft, +.dataflowNodeMiddleRight, +.dataflowHub { + top: 50%; +} + +.dataflowNodeBottomLeft, +.dataflowNodeBottomRight { + top: 84%; +} + +.dataflowHub { + left: 50%; + width: clamp(6rem, 13vw, 9rem); +} + +.dataflowHub img { + width: clamp(3rem, 7vw, 5.4rem); + height: clamp(3rem, 7vw, 5.4rem); +} + +@keyframes dataflow-signal { + to { + stroke-dashoffset: -1; + } +} + +@keyframes dataflow-icon-cycle { + 0%, + 3.7% { + opacity: 1; + transform: scale(1); + } + + 4.2%, + 99% { + opacity: 0; + transform: scale(0.82); + } +} + :global([data-theme="dark"]) .resourceMeterCritical .resourceLabel, :global([data-theme="dark"]) .containerRowFailed { color: #ffe3e3; @@ -280,8 +446,39 @@ section { } } -@media (prefers-reduced-motion: reduce) { - .statusDotFailed { - animation: none; +@media screen and (max-width: 640px) { + .dataflowSection { + padding-block: 4rem; + } + + .dataflowHeading { + margin-bottom: 2rem; + } + + .dataflowDiagram { + aspect-ratio: 4 / 5; + } + + .dataflowNode { + width: 4.5rem; + padding: 0.6rem; + } +} + +@media (prefers-reduced-motion: reduce) { + .statusDotFailed, + .dataflowSignal, + .dataflowSignalReverse, + .dataflowCyclingIcons img { + animation: none; + } + + .dataflowSignal, + .dataflowSignalReverse { + display: none; + } + + .dataflowCyclingIcons img:first-child { + opacity: 1; } } diff --git a/apps/docs/src/pages/index.tsx b/apps/docs/src/pages/index.tsx index 7f5426c10..06b85d6ce 100644 --- a/apps/docs/src/pages/index.tsx +++ b/apps/docs/src/pages/index.tsx @@ -5,9 +5,9 @@ import Layout from "@theme/Layout"; import HomepageFeatures from "../components/pages/home/features/features"; import HomepageUserReviews from "../components/pages/home/review-list/review-list"; import HomeHero from "../components/pages/home/hero/hero"; +import { DataflowVisualizationComponent } from "../components/pages/home/dataflow/dataflow"; import styles from "./index.module.css"; -import BrowserOnly from "@docusaurus/BrowserOnly"; import { SectionContainer } from "@site/src/components/pages/home/container/section-container"; import { DragAndDropShowcase } from "@site/src/components/pages/home/drag-and-drop/drag-and-drop-showcase"; import { AvailableIntegrations } from "@site/src/components/pages/home/integrations/available-integrations"; @@ -32,13 +32,7 @@ export default function Home() { - loading...}> - {() => { - const DataflowVisualizationComponent = - require("../components/pages/home/dataflow/dataflow").DataflowVisualizationComponent; - return ; - }} - +