From 56731258ef56c4ed1a11cc1328b537e351ac49e8 Mon Sep 17 00:00:00 2001 From: Beebles <102569435+beebls@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:21:47 -0600 Subject: [PATCH 1/2] add `onDocsClick` param for custom TitleViews --- frontend/src/components/Docs.tsx | 118 +++++++++------- frontend/src/components/Scrollable.tsx | 188 ++++++++++++------------- frontend/src/components/TitleView.tsx | 40 +++--- 3 files changed, 176 insertions(+), 170 deletions(-) diff --git a/frontend/src/components/Docs.tsx b/frontend/src/components/Docs.tsx index de06d6b67..333b52eaf 100644 --- a/frontend/src/components/Docs.tsx +++ b/frontend/src/components/Docs.tsx @@ -1,17 +1,18 @@ +import { SidebarNavigation, SteamSpinner, useParams } from 'decky-frontend-lib'; +import i18n from 'i18next'; import { VFC, useEffect, useState } from 'react'; -import { useParams, SidebarNavigation, SteamSpinner } from "decky-frontend-lib"; import { lazy } from 'react'; -import i18n from 'i18next'; -import { ScrollArea, Scrollable, scrollableRef } from "./Scrollable"; +import { ScrollArea, Scrollable, scrollableRef } from './Scrollable'; + const MarkdownRenderer = lazy(() => import('./Markdown')); const DocsPage: VFC<{ content: string }> = ({ content }) => { - const ref = scrollableRef(); + const ref = scrollableRef(); - return ( - <> - - - - - - - - ) -} + + + + + + + + ); +}; interface DocsPage { title: string; @@ -39,49 +40,60 @@ interface DocsPage { } const StorePage: VFC<{}> = () => { + const [docs, setDocs] = useState<(DocsPage | 'separator')[] | null>(null); + const { plugin } = useParams<{ plugin: string }>(); - const [docs, setDocs] = useState<(DocsPage | 'separator')[] | null>(null); - const { plugin } = useParams<{ plugin: string }>() - - useEffect(() => { + useEffect(() => { (async () => { - setDocs(await (await fetch(`http://127.0.0.1:1337/docs/${plugin}/${i18n.resolvedLanguage}`, { - method: 'GET', - credentials: 'include', - headers: { - 'Content-Type': 'application/json', - Authentication: window.deckyAuthToken, - } - })).json()) - })(); - }, []); + setDocs( + await ( + await fetch(`http://127.0.0.1:1337/docs/${plugin}/${i18n.resolvedLanguage}`, { + method: 'GET', + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + Authentication: window.deckyAuthToken, + }, + }) + ).json(), + ); + })(); + }, []); - return ( - <> - {!docs ? + return ( + <> + {!docs ? (
- : (docs.length == 1) ? -
- -
- : - ( - file == 'separator' ? 'separator' : { - title: file["title"], - content:, - route: `/decky/docs/${plugin}/${file["title"]}`, - hideTitle: true, - } - ))} - /> - } + ) : docs.length == 1 ? ( +
+ +
+ ) : ( + + file == 'separator' + ? 'separator' + : { + title: file['title'], + content: , + route: `/decky/docs/${plugin}/${file['title']}`, + hideTitle: true, + }, + )} + /> + )} - ) -} + ); +}; export default StorePage; diff --git a/frontend/src/components/Scrollable.tsx b/frontend/src/components/Scrollable.tsx index dcacb3cc9..4fd948361 100644 --- a/frontend/src/components/Scrollable.tsx +++ b/frontend/src/components/Scrollable.tsx @@ -1,46 +1,38 @@ +import { Focusable, FocusableProps, GamepadButton, GamepadEvent, ServerAPI } from 'decky-frontend-lib'; // this file was mostly (the good bits) made by davocarli#7308 // it should probably be moved to DFL eventually -import { FC, ForwardRefExoticComponent } from "react" -import { - Focusable, - FocusableProps, - GamepadEvent, - GamepadButton, - ServerAPI, -} from "decky-frontend-lib" -import React, { useRef } from "react" +import { FC, ForwardRefExoticComponent } from 'react'; +import React, { useRef } from 'react'; -const DEFAULTSCROLLSPEED = 50 +const DEFAULTSCROLLSPEED = 50; export interface ScrollableElement extends HTMLDivElement {} export function scrollableRef() { - return useRef(null) + return useRef(null); } -export const Scrollable: ForwardRefExoticComponent = React.forwardRef( - (props, ref) => { - if (!props.style) { - props.style = {} - } - // props.style.minHeight = '100%'; - // props.style.maxHeight = '80%'; - props.style.height = "85vh" // was 95vh previously! - props.style.overflowY = "scroll" +export const Scrollable: ForwardRefExoticComponent = React.forwardRef((props, ref) => { + if (!props.style) { + props.style = {}; + } + // props.style.minHeight = '100%'; + // props.style.maxHeight = '80%'; + props.style.height = '85vh'; // was 95vh previously! + props.style.overflowY = 'scroll'; - return ( - -
- - ) - } -) + return ( + +
+ + ); +}); interface ScrollAreaProps extends FocusableProps { - scrollable: React.RefObject - scrollSpeed?: number - serverApi?: ServerAPI - noFocusRing?: boolean + scrollable: React.RefObject; + scrollSpeed?: number; + serverApi?: ServerAPI; + noFocusRing?: boolean; } // const writeLog = async (serverApi: ServerAPI, content: any) => { @@ -49,81 +41,75 @@ interface ScrollAreaProps extends FocusableProps { // } const scrollOnDirection = ( - e: GamepadEvent, - ref: React.RefObject, - amt: number, - prev: React.RefObject, - next: React.RefObject + e: GamepadEvent, + ref: React.RefObject, + amt: number, + prev: React.RefObject, + next: React.RefObject, ) => { - let childNodes = ref.current?.childNodes - let currentIndex = null - childNodes?.forEach((node, i) => { - if (node == e.currentTarget) { - currentIndex = i - } - }) + let childNodes = ref.current?.childNodes; + let currentIndex = null; + childNodes?.forEach((node, i) => { + if (node == e.currentTarget) { + currentIndex = i; + } + }); - // @ts-ignore - let pos = e.currentTarget?.getBoundingClientRect() - let out = ref.current?.getBoundingClientRect() + // @ts-ignore + let pos = e.currentTarget?.getBoundingClientRect(); + let out = ref.current?.getBoundingClientRect(); - if (e.detail.button == GamepadButton.DIR_DOWN) { - if ( - out?.bottom != undefined && - pos.bottom <= out.bottom && - currentIndex != null && - childNodes != undefined && - currentIndex + 1 < childNodes.length - ) { - next.current?.focus() - } else { - ref.current?.scrollBy({ top: amt, behavior: "auto" }) - } - } else if (e.detail.button == GamepadButton.DIR_UP) { - if ( - out?.top != undefined && - pos.top >= out.top && - currentIndex != null && - childNodes != undefined && - currentIndex - 1 >= 0 - ) { - prev.current?.focus() - } else { - ref.current?.scrollBy({ top: -amt, behavior: "auto" }) - } - } else if (e.detail.button == GamepadButton.DIR_LEFT) { - throw("this is not a real error, just a (temporary?) workaround to make navigation work in docs") - } -} + if (e.detail.button == GamepadButton.DIR_DOWN) { + if ( + out?.bottom != undefined && + pos.bottom <= out.bottom && + currentIndex != null && + childNodes != undefined && + currentIndex + 1 < childNodes.length + ) { + next.current?.focus(); + } else { + ref.current?.scrollBy({ top: amt, behavior: 'auto' }); + } + } else if (e.detail.button == GamepadButton.DIR_UP) { + if ( + out?.top != undefined && + pos.top >= out.top && + currentIndex != null && + childNodes != undefined && + currentIndex - 1 >= 0 + ) { + prev.current?.focus(); + } else { + ref.current?.scrollBy({ top: -amt, behavior: 'auto' }); + } + } else if (e.detail.button == GamepadButton.DIR_LEFT) { + throw 'this is not a real error, just a (temporary?) workaround to make navigation work in docs'; + } +}; export const ScrollArea: FC = (props) => { - let scrollSpeed = DEFAULTSCROLLSPEED - if (props.scrollSpeed) { - scrollSpeed = props.scrollSpeed - } + let scrollSpeed = DEFAULTSCROLLSPEED; + if (props.scrollSpeed) { + scrollSpeed = props.scrollSpeed; + } - const prevFocus = useRef(null) - const nextFocus = useRef(null) + const prevFocus = useRef(null); + const nextFocus = useRef(null); - props.onActivate = (e) => { - const ele = e.currentTarget as HTMLElement - ele.focus() - } - props.onGamepadDirection = (e) => { - scrollOnDirection( - e, - props.scrollable, - scrollSpeed, - prevFocus, - nextFocus - ) - } + props.onActivate = (e) => { + const ele = e.currentTarget as HTMLElement; + ele.focus(); + }; + props.onGamepadDirection = (e) => { + scrollOnDirection(e, props.scrollable, scrollSpeed, prevFocus, nextFocus); + }; - return ( - - {}} /> - - {}} /> - - ) -} + return ( + + {}} /> + + {}} /> + + ); +}; diff --git a/frontend/src/components/TitleView.tsx b/frontend/src/components/TitleView.tsx index 293b9922e..6f02aa225 100644 --- a/frontend/src/components/TitleView.tsx +++ b/frontend/src/components/TitleView.tsx @@ -1,8 +1,8 @@ import { DialogButton, Focusable, Router, staticClasses } from 'decky-frontend-lib'; -import { CSSProperties, VFC } from 'react'; +import { CSSProperties, VFC, cloneElement } from 'react'; import { useTranslation } from 'react-i18next'; import { BsGearFill } from 'react-icons/bs'; -import { FaArrowLeft, FaStore, FaInfo } from 'react-icons/fa'; +import { FaArrowLeft, FaInfo, FaStore } from 'react-icons/fa'; import { useDeckyState } from './DeckyState'; @@ -53,21 +53,29 @@ const TitleView: VFC = () => { ); } + const CustomTitleView = activePlugin?.titleView + ? cloneElement(activePlugin.titleView, { onDocsClick: onInfoClick }) + : null; + return ( - - - - - {activePlugin?.titleView ||
{activePlugin.name}
} - - - + + + + + {CustomTitleView || ( + <> +
{activePlugin.name}
+ + + + + )}
); }; From d6ae395d535578b1a24a85b06c4a2d0a3a3dd137 Mon Sep 17 00:00:00 2001 From: Beebles <102569435+beebls@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:22:10 -0600 Subject: [PATCH 2/2] switch `Router` to `Navigation` --- frontend/src/components/TitleView.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/TitleView.tsx b/frontend/src/components/TitleView.tsx index 6f02aa225..82386339d 100644 --- a/frontend/src/components/TitleView.tsx +++ b/frontend/src/components/TitleView.tsx @@ -1,4 +1,4 @@ -import { DialogButton, Focusable, Router, staticClasses } from 'decky-frontend-lib'; +import { DialogButton, Focusable, Navigation, staticClasses } from 'decky-frontend-lib'; import { CSSProperties, VFC, cloneElement } from 'react'; import { useTranslation } from 'react-i18next'; import { BsGearFill } from 'react-icons/bs'; @@ -17,18 +17,18 @@ const TitleView: VFC = () => { const { t } = useTranslation(); const onSettingsClick = () => { - Router.CloseSideMenus(); - Router.Navigate('/decky/settings'); + Navigation.CloseSideMenus(); + Navigation.Navigate('/decky/settings'); }; const onStoreClick = () => { - Router.CloseSideMenus(); - Router.Navigate('/decky/store'); + Navigation.CloseSideMenus(); + Navigation.Navigate('/decky/store'); }; const onInfoClick = () => { - Router.CloseSideMenus(); - Router.Navigate(`/decky/docs/${activePlugin?.name}`); + Navigation.CloseSideMenus(); + Navigation.Navigate(`/decky/docs/${activePlugin?.name}`); }; if (activePlugin === null) {