Compare commits

..

7 Commits

Author SHA1 Message Date
semantic-release-bot
9b368c5f11 chore(release): 3.9.0 [CI SKIP] 2022-11-16 20:45:13 +00:00
Lukas Senionis
e167ef5a13 feat(Dialog): add "focusable" button prop (#51) 2022-11-16 15:44:36 -05:00
semantic-release-bot
2f3df00967 chore(release): 3.8.0 [CI SKIP] 2022-11-11 21:05:54 +00:00
AAGaming
215156d316 feat(routerhook): add global components support 2022-11-11 16:05:05 -05:00
semantic-release-bot
47d75db690 chore(release): 3.7.14 [CI SKIP] 2022-11-05 01:47:25 +00:00
AAGaming
82768e0415 fix(Menu): fix on Steam beta 2022-11-04 21:46:39 -04:00
AAGaming
e44187fe4b fix(Modal): fix on Steam beta 2022-11-04 21:46:22 -04:00
6 changed files with 135 additions and 22 deletions

View File

@@ -1,3 +1,25 @@
# [3.9.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.8.0...v3.9.0) (2022-11-16)
### Features
* **Dialog:** add "focusable" button prop ([#51](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/51)) ([e167ef5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e167ef5a138a3edc004db2365334f8455c177132))
# [3.8.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.14...v3.8.0) (2022-11-11)
### Features
* **routerhook:** add global components support ([215156d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/215156d31688faac9028627379e5a3ac4d64ec46))
## [3.7.14](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.13...v3.7.14) (2022-11-05)
### Bug Fixes
* **Menu:** fix on Steam beta ([82768e0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/82768e0415d084deb2af39beb3f9273a83e819de))
* **Modal:** fix on Steam beta ([e44187f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e44187fe4b9d3e3c9e94490669591599dc5246ba))
## [3.7.13](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.12...v3.7.13) (2022-11-02)

View File

@@ -1,6 +1,6 @@
{
"name": "decky-frontend-lib",
"version": "3.7.13",
"version": "3.9.0",
"description": "A library for building decky plugins",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@@ -9,8 +9,35 @@ export interface DialogCommonProps extends RefAttributes<HTMLDivElement> {
}
export interface DialogButtonProps extends DialogCommonProps, FooterLegendProps {
/**
* Enables/disables the focus around the button.
*
* @note
* Default value depends on context, so setting it to `false` will enable it.
*/
noFocusRing?: boolean;
/**
* Disables the button - assigned `on*` methods will not be invoked if clicked.
*
* @note
* Depending on where it is, it might still get focus. In such case it can be
* partially disabled separately.
*
* @see focusable.
*/
disabled?: boolean;
/**
* Enables/disables the navigation based focus on button - you won't be able to navigate to
* it via the gamepad or keyboard.
*
* @note
* If set to `false`, it still can be clicked and **WILL** become focused until navigated away.
* Depending on the context of where the button is, even a disabled button can focused.
*/
focusable?: boolean;
onClick?(e: MouseEvent): void;
onPointerDown?(e: PointerEvent): void;
onPointerUp?(e: PointerEvent): void;

View File

@@ -1,5 +1,6 @@
import { FC, ReactNode } from 'react';
import { fakeRenderComponent } from '../utils';
import { findModuleChild } from '../webpack';
export const showContextMenu: (children: ReactNode, parent?: EventTarget) => void = findModuleChild((m) => {
@@ -38,7 +39,11 @@ export const MenuGroup: FC<MenuGroupProps> = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.prototype?.RenderSubMenu && m[prop]?.prototype?.ShowSubMenu) {
if (
(m[prop]?.toString()?.includes('bInGamepadUI:') &&
fakeRenderComponent(() => m[prop]())?.type?.prototype?.RenderSubMenu) ||
(m[prop]?.prototype?.RenderSubMenu && m[prop]?.prototype?.ShowSubMenu)
) {
return m[prop];
}
}
@@ -54,7 +59,10 @@ export const MenuItem: FC<MenuItemProps> = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.prototype?.OnOKButton && m[prop]?.prototype?.OnMouseEnter) {
if (
m[prop]?.render?.toString()?.includes('bPlayAudio:') ||
(m[prop]?.prototype?.OnOKButton && m[prop]?.prototype?.OnMouseEnter)
) {
return m[prop];
}
}

View File

@@ -1,7 +1,7 @@
import { FC, ReactNode } from 'react';
import { findSP } from '../utils';
import { findModuleChild } from '../webpack';
import { findModule, findModuleChild } from '../webpack';
// All of the popout options + strTitle are related. Proper usage is not yet known...
export interface ShowModalProps {
@@ -29,18 +29,57 @@ export interface ShowModalResult {
Update: (modal: ReactNode) => void;
}
const showModalRaw: (modal: ReactNode, parent?: EventTarget, props?: ShowModalProps) => Promise<ShowModalResult> =
findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (typeof m[prop] === 'function' && m[prop].toString().includes('bHideMainWindowForPopouts:!0')) {
return m[prop];
}
const showModalRaw:
| ((
modal: ReactNode,
parent?: EventTarget,
title?: string,
props?: ShowModalProps,
unknown1?: unknown,
hideActions?: { bHideActions?: boolean },
modalManager?: unknown,
) => Promise<ShowModalResult>)
| void = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (
typeof m[prop] === 'function' &&
m[prop].toString().includes('props.bDisableBackgroundDismiss') &&
!m[prop]?.prototype?.Cancel
) {
return m[prop];
}
});
}
});
export const showModal = (modal: ReactNode, parent?: EventTarget, props?: ShowModalProps): Promise<ShowModalResult> => {
return showModalRaw(modal, parent || findSP(), props);
const oldShowModalRaw:
| ((modal: ReactNode, parent?: EventTarget, props?: ShowModalProps) => Promise<ShowModalResult>)
| void = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (typeof m[prop] === 'function' && m[prop].toString().includes('bHideMainWindowForPopouts:!0')) {
return m[prop];
}
}
});
export const showModal = (
modal: ReactNode,
parent?: EventTarget,
props: ShowModalProps = {
strTitle: 'Decky Dialog',
bHideMainWindowForPopouts: false,
},
): Promise<ShowModalResult> => {
if (showModalRaw) {
return showModalRaw(modal, parent || findSP(), props.strTitle, props, undefined, {
bHideActions: props.bHideActionIcons,
});
} else if (oldShowModalRaw) {
return oldShowModalRaw(modal, parent || findSP(), props);
} else {
throw new Error('[DFL:Modals]: Cannot find showModal function');
}
};
export interface ModalRootProps {
@@ -79,11 +118,26 @@ export const ConfirmModal = findModuleChild((m) => {
}
}) as FC<ConfirmModalProps>;
export const ModalRoot = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.prototype?.OK && m[prop]?.prototype?.Cancel && m[prop]?.prototype?.render) {
return m[prop];
// new
export const ModalRoot = (Object.values(
findModule((m: any) => {
if (typeof m !== 'object') return false;
for (let prop in m) {
if (m[prop]?.toString()?.includes('"ModalManager","DialogWrapper"')) {
return true;
}
}
}
}) as FC<ModalRootProps>;
return false;
}) || {},
)?.find((x: any) => x?.type?.toString()?.includes('((function(){')) ||
// old
findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.prototype?.OK && m[prop]?.prototype?.Cancel && m[prop]?.prototype?.render) {
return m[prop];
}
}
})) as FC<ModalRootProps>;

View File

@@ -26,8 +26,10 @@ export type RoutePatch = (route: RouteProps) => RouteProps;
export interface RouterHook {
addRoute(path: string, component: ComponentType, props?: Omit<RouteProps, 'path' | 'children'>): void;
addPatch(path: string, patch: RoutePatch): RoutePatch;
removePatch(path: string, patch: RoutePatch): void;
addGlobalComponent(name: string, component: ComponentType): void;
removeRoute(path: string): void;
removePatch(path: string, patch: RoutePatch): void;
removeGlobalComponent(name: string): void;
}
export interface ToastData {