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) {