@decky/ui 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +504 -0
  2. package/README.md +27 -0
  3. package/dist/class-mapper.d.ts +7 -0
  4. package/dist/class-mapper.js +26 -0
  5. package/dist/components/Button.d.ts +5 -0
  6. package/dist/components/Button.js +2 -0
  7. package/dist/components/ButtonItem.d.ts +7 -0
  8. package/dist/components/ButtonItem.js +3 -0
  9. package/dist/components/Carousel.d.ts +19 -0
  10. package/dist/components/Carousel.js +2 -0
  11. package/dist/components/ControlsList.d.ts +6 -0
  12. package/dist/components/ControlsList.js +2 -0
  13. package/dist/components/Dialog.d.ts +32 -0
  14. package/dist/components/Dialog.js +19 -0
  15. package/dist/components/DialogCheckbox.d.ts +17 -0
  16. package/dist/components/DialogCheckbox.js +14 -0
  17. package/dist/components/Dropdown.d.ts +34 -0
  18. package/dist/components/Dropdown.js +3 -0
  19. package/dist/components/Field.d.ts +22 -0
  20. package/dist/components/Field.js +2 -0
  21. package/dist/components/FocusRing.d.ts +9 -0
  22. package/dist/components/FocusRing.js +2 -0
  23. package/dist/components/Focusable.d.ts +12 -0
  24. package/dist/components/Focusable.js +2 -0
  25. package/dist/components/FooterLegend.d.ts +66 -0
  26. package/dist/components/FooterLegend.js +32 -0
  27. package/dist/components/Item.d.ts +11 -0
  28. package/dist/components/Item.js +1 -0
  29. package/dist/components/Marquee.d.ts +14 -0
  30. package/dist/components/Marquee.js +2 -0
  31. package/dist/components/Menu.d.ts +29 -0
  32. package/dist/components/Menu.js +8 -0
  33. package/dist/components/Modal.d.ts +52 -0
  34. package/dist/components/Modal.js +29 -0
  35. package/dist/components/Panel.d.ts +11 -0
  36. package/dist/components/Panel.js +4 -0
  37. package/dist/components/ProgressBar.d.ts +21 -0
  38. package/dist/components/ProgressBar.js +4 -0
  39. package/dist/components/Scroll.d.ts +7 -0
  40. package/dist/components/Scroll.js +5 -0
  41. package/dist/components/SidebarNavigation.d.ts +21 -0
  42. package/dist/components/SidebarNavigation.js +2 -0
  43. package/dist/components/SliderField.d.ts +26 -0
  44. package/dist/components/SliderField.js +2 -0
  45. package/dist/components/Spinner.d.ts +2 -0
  46. package/dist/components/Spinner.js +2 -0
  47. package/dist/components/SteamSpinner.d.ts +2 -0
  48. package/dist/components/SteamSpinner.js +2 -0
  49. package/dist/components/Tabs.d.ts +16 -0
  50. package/dist/components/Tabs.js +48 -0
  51. package/dist/components/TextField.d.ts +22 -0
  52. package/dist/components/TextField.js +2 -0
  53. package/dist/components/Toggle.d.ts +8 -0
  54. package/dist/components/Toggle.js +2 -0
  55. package/dist/components/ToggleField.d.ts +9 -0
  56. package/dist/components/ToggleField.js +2 -0
  57. package/dist/components/index.d.ts +25 -0
  58. package/dist/components/index.js +25 -0
  59. package/dist/custom-components/ColorPickerModal.d.ts +12 -0
  60. package/dist/custom-components/ColorPickerModal.js +87 -0
  61. package/dist/custom-components/ReorderableList.d.ts +26 -0
  62. package/dist/custom-components/ReorderableList.js +84 -0
  63. package/dist/custom-components/SuspensefulImage.d.ts +7 -0
  64. package/dist/custom-components/SuspensefulImage.js +27 -0
  65. package/dist/custom-components/index.d.ts +3 -0
  66. package/dist/custom-components/index.js +3 -0
  67. package/dist/custom-hooks/index.d.ts +1 -0
  68. package/dist/custom-hooks/index.js +1 -0
  69. package/dist/custom-hooks/useQuickAccessVisible.d.ts +1 -0
  70. package/dist/custom-hooks/useQuickAccessVisible.js +22 -0
  71. package/dist/deck-hooks/index.d.ts +1 -0
  72. package/dist/deck-hooks/index.js +1 -0
  73. package/dist/deck-hooks/useParams.d.ts +1 -0
  74. package/dist/deck-hooks/useParams.js +2 -0
  75. package/dist/globals/SteamClient.d.ts +307 -0
  76. package/dist/globals/SteamClient.js +1 -0
  77. package/dist/globals/index.d.ts +2 -0
  78. package/dist/globals/index.js +2 -0
  79. package/dist/globals/stores.d.ts +50 -0
  80. package/dist/globals/stores.js +1 -0
  81. package/dist/index.d.ts +10 -0
  82. package/dist/index.js +10 -0
  83. package/dist/logger.d.ts +17 -0
  84. package/dist/logger.js +46 -0
  85. package/dist/modules/Router.d.ts +110 -0
  86. package/dist/modules/Router.js +102 -0
  87. package/dist/modules/index.d.ts +1 -0
  88. package/dist/modules/index.js +1 -0
  89. package/dist/plugin.d.ts +68 -0
  90. package/dist/plugin.js +5 -0
  91. package/dist/utils/index.d.ts +12 -0
  92. package/dist/utils/index.js +23 -0
  93. package/dist/utils/patcher.d.ts +18 -0
  94. package/dist/utils/patcher.js +103 -0
  95. package/dist/utils/react.d.ts +18 -0
  96. package/dist/utils/react.js +74 -0
  97. package/dist/utils/static-classes.d.ts +52 -0
  98. package/dist/utils/static-classes.js +28 -0
  99. package/dist/webpack.d.ts +21 -0
  100. package/dist/webpack.js +102 -0
  101. package/package.json +82 -0
  102. package/src/class-mapper.ts +34 -0
  103. package/src/components/Button.tsx +8 -0
  104. package/src/components/ButtonItem.tsx +14 -0
  105. package/src/components/Carousel.ts +25 -0
  106. package/src/components/ControlsList.tsx +13 -0
  107. package/src/components/Dialog.tsx +86 -0
  108. package/src/components/DialogCheckbox.tsx +36 -0
  109. package/src/components/Dropdown.tsx +49 -0
  110. package/src/components/Field.tsx +28 -0
  111. package/src/components/FocusRing.ts +15 -0
  112. package/src/components/Focusable.tsx +18 -0
  113. package/src/components/FooterLegend.ts +67 -0
  114. package/src/components/Item.tsx +12 -0
  115. package/src/components/Marquee.tsx +20 -0
  116. package/src/components/Menu.tsx +57 -0
  117. package/src/components/Modal.tsx +119 -0
  118. package/src/components/Panel.tsx +26 -0
  119. package/src/components/ProgressBar.tsx +35 -0
  120. package/src/components/Scroll.tsx +15 -0
  121. package/src/components/SidebarNavigation.tsx +28 -0
  122. package/src/components/SliderField.tsx +33 -0
  123. package/src/components/Spinner.tsx +8 -0
  124. package/src/components/SteamSpinner.tsx +7 -0
  125. package/src/components/Tabs.tsx +127 -0
  126. package/src/components/TextField.tsx +28 -0
  127. package/src/components/Toggle.tsx +14 -0
  128. package/src/components/ToggleField.tsx +15 -0
  129. package/src/components/index.ts +25 -0
  130. package/src/custom-components/ColorPickerModal.tsx +132 -0
  131. package/src/custom-components/ReorderableList.tsx +183 -0
  132. package/src/custom-components/SuspensefulImage.tsx +44 -0
  133. package/src/custom-components/index.ts +3 -0
  134. package/src/custom-hooks/index.ts +1 -0
  135. package/src/custom-hooks/useQuickAccessVisible.tsx +63 -0
  136. package/src/deck-hooks/index.ts +1 -0
  137. package/src/deck-hooks/useParams.ts +15 -0
  138. package/src/globals/SteamClient.ts +321 -0
  139. package/src/globals/index.ts +2 -0
  140. package/src/globals/stores.ts +50 -0
  141. package/src/index.ts +11 -0
  142. package/src/logger.ts +77 -0
  143. package/src/modules/Router.tsx +174 -0
  144. package/src/modules/index.ts +1 -0
  145. package/src/plugin.tsx +96 -0
  146. package/src/utils/index.ts +43 -0
  147. package/src/utils/patcher.ts +160 -0
  148. package/src/utils/react.ts +119 -0
  149. package/src/utils/static-classes.ts +1104 -0
  150. package/src/webpack.ts +136 -0
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@decky/ui",
3
+ "version": "4.0.0",
4
+ "description": "A library for interacting with the Steam frontend in Decky plugins and elsewhere.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "module": "dist/index.js",
8
+ "sideEffects": false,
9
+ "scripts": {
10
+ "build": "shx rm -rf dist && tsc -b",
11
+ "dev": "tsc -b -w",
12
+ "docs": "typedoc --theme wiki-js --tsconfig ./tsconfig.json src/**/*",
13
+ "test": "echo 'No tests for now!'",
14
+ "prepare": "husky install",
15
+ "commit": "git-cz"
16
+ },
17
+ "files": [
18
+ "/lib",
19
+ "/src",
20
+ "/dist"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/SteamDeckHomebrew/decky-frontend-lib.git"
25
+ },
26
+ "keywords": [
27
+ "decky",
28
+ "steam-deck",
29
+ "steam",
30
+ "components"
31
+ ],
32
+ "author": "SteamDeckHomebrew Team",
33
+ "license": "LGPL-2.1",
34
+ "bugs": {
35
+ "url": "https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues"
36
+ },
37
+ "homepage": "https://github.com/SteamDeckHomebrew/decky-frontend-lib#readme",
38
+ "config": {
39
+ "commitizen": {
40
+ "path": "@commitlint/cz-commitlint"
41
+ }
42
+ },
43
+ "devDependencies": {
44
+ "@commitlint/cli": "^17.0.2",
45
+ "@commitlint/config-conventional": "^17.0.2",
46
+ "@commitlint/cz-commitlint": "^17.0.0",
47
+ "@semantic-release/changelog": "^6.0.1",
48
+ "@semantic-release/git": "^10.0.1",
49
+ "@types/jest": "^27.4.1",
50
+ "@types/react": "16.14.0",
51
+ "@types/react-router": "5.1.18",
52
+ "commitizen": "^4.2.4",
53
+ "husky": "^8.0.1",
54
+ "import-sort-style-module": "^6.0.0",
55
+ "jest": "^27.5.1",
56
+ "minimist": "^1.2.6",
57
+ "prettier": "^2.7.1",
58
+ "prettier-plugin-import-sort": "^0.0.7",
59
+ "semantic-release": "^19.0.3",
60
+ "shx": "^0.3.4",
61
+ "ts-jest": "^27.1.4",
62
+ "typedoc": "^0.23.24",
63
+ "typedoc-plugin-mdn-links": "^2.0.0",
64
+ "typedoc-plugin-missing-exports": "^1.0.0",
65
+ "typedoc-wikijs-theme": "^1.0.5",
66
+ "typescript": "^4.9.5"
67
+ },
68
+ "pnpm": {
69
+ "peerDependencyRules": {
70
+ "ignoreMissing": [
71
+ "react",
72
+ "react-dom"
73
+ ]
74
+ }
75
+ },
76
+ "importSort": {
77
+ ".js, .jsx, .ts, .tsx": {
78
+ "style": "module",
79
+ "parser": "typescript"
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,34 @@
1
+ import { Module, findAllModules } from './webpack';
2
+
3
+ export interface ClassModule {
4
+ [name: string]: string;
5
+ }
6
+
7
+ export const classMap: ClassModule[] = findAllModules((m: Module) => {
8
+ if (typeof m == 'object' && !m.__esModule) {
9
+ const keys = Object.keys(m);
10
+ // special case some libraries
11
+ if (keys.length == 1 && m.version) return false;
12
+ // special case localization
13
+ if (keys.length > 1000 && m.AboutSettings) return false;
14
+
15
+ return keys.length > 0 && keys.every((k) => !Object.getOwnPropertyDescriptor(m, k)?.get && typeof m[k] == 'string');
16
+ }
17
+ return false;
18
+ });
19
+
20
+ export function findClass(name: string): string | void {
21
+ return classMap.find((m) => m?.[name])?.[name];
22
+ }
23
+
24
+ export function findClassModule(filter: (module: any) => boolean): ClassModule | void {
25
+ return classMap.find((m) => filter(m));
26
+ }
27
+
28
+ export function unminifyClass(minifiedClass: string): string | void {
29
+ for (let m of classMap) {
30
+ for (let className of Object.keys(m)) {
31
+ if (m[className] == minifiedClass) return className;
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,8 @@
1
+ import { FC } from 'react';
2
+
3
+ import { DialogButton, DialogButtonProps } from './Dialog';
4
+
5
+ export interface ButtonProps extends DialogButtonProps {}
6
+
7
+ // Button isn't exported, so call DialogButton to grab it
8
+ export const Button = (DialogButton as any)?.render({}).type as FC<ButtonProps>;
@@ -0,0 +1,14 @@
1
+ import { FC } from 'react';
2
+
3
+ import { CommonUIModule } from '../webpack';
4
+ import { ItemProps } from './Item';
5
+
6
+ export interface ButtonItemProps extends ItemProps {
7
+ onClick?(e: MouseEvent): void;
8
+ disabled?: boolean;
9
+ }
10
+ export const ButtonItem = Object.values(CommonUIModule).find(
11
+ (mod: any) =>
12
+ mod?.render?.toString()?.includes('"highlightOnFocus","childrenContainerWidth"') ||
13
+ mod?.render?.toString()?.includes('childrenContainerWidth:"min"'),
14
+ ) as FC<ButtonItemProps>;
@@ -0,0 +1,25 @@
1
+ import { HTMLAttributes, ReactNode, RefAttributes, VFC } from 'react';
2
+
3
+ import { Export, findModuleExport } from '../webpack';
4
+
5
+ export interface CarouselProps extends HTMLAttributes<HTMLDivElement> {
6
+ autoFocus?: boolean;
7
+ enableBumperPaging?: boolean;
8
+ fnDoesItemTakeFocus?: (...unknown: any[]) => boolean;
9
+ fnGetColumnWidth?: (...unknown: any[]) => number;
10
+ fnGetId?: (id: number) => number;
11
+ fnItemRenderer?: (id: number, ...unknown: any[]) => ReactNode;
12
+ fnUpdateArrows?: (...unknown: any[]) => any;
13
+ initialColumn?: number;
14
+ nHeight?: number;
15
+ nIndexLeftmost?: number;
16
+ nItemHeight?: number;
17
+ nItemMarginX?: number;
18
+ nNumItems?: number;
19
+ name?: string;
20
+ scrollToAlignment?: 'center';
21
+ }
22
+
23
+ export const Carousel = findModuleExport((e: Export) => e.render?.toString().includes('setFocusedColumn:')) as VFC<
24
+ CarouselProps & RefAttributes<HTMLDivElement>
25
+ >;
@@ -0,0 +1,13 @@
1
+ import { FC } from 'react';
2
+
3
+ import { Export, findModuleExport } from '../webpack';
4
+
5
+ export interface ControlsListProps {
6
+ alignItems?: 'left' | 'right' | 'center';
7
+ spacing?: 'standard' | 'extra';
8
+ }
9
+
10
+ export const ControlsList: FC<ControlsListProps> = findModuleExport(
11
+ (e: Export) =>
12
+ e?.toString && e.toString().includes('().ControlsListChild') && e.toString().includes('().ControlsListOuterPanel'),
13
+ );
@@ -0,0 +1,86 @@
1
+ import { CSSProperties, FC, RefAttributes } from 'react';
2
+
3
+ import { CommonUIModule } from '../webpack';
4
+ import { FooterLegendProps } from './FooterLegend';
5
+
6
+ export interface DialogCommonProps extends RefAttributes<HTMLDivElement> {
7
+ style?: CSSProperties;
8
+ className?: string;
9
+ }
10
+
11
+ export interface DialogButtonProps extends DialogCommonProps, FooterLegendProps {
12
+ /**
13
+ * Enables/disables the focus around the button.
14
+ *
15
+ * @note
16
+ * Default value depends on context, so setting it to `false` will enable it.
17
+ */
18
+ noFocusRing?: boolean;
19
+
20
+ /**
21
+ * Disables the button - assigned `on*` methods will not be invoked if clicked.
22
+ *
23
+ * @note
24
+ * Depending on where it is, it might still get focus. In such case it can be
25
+ * partially disabled separately.
26
+ *
27
+ * @see focusable.
28
+ */
29
+ disabled?: boolean;
30
+
31
+ /**
32
+ * Enables/disables the navigation based focus on button - you won't be able to navigate to
33
+ * it via the gamepad or keyboard.
34
+ *
35
+ * @note
36
+ * If set to `false`, it still can be clicked and **WILL** become focused until navigated away.
37
+ * Depending on the context of where the button is, even a disabled button can focused.
38
+ */
39
+ focusable?: boolean;
40
+
41
+ onClick?(e: MouseEvent): void;
42
+ onPointerDown?(e: PointerEvent): void;
43
+ onPointerUp?(e: PointerEvent): void;
44
+ onPointerCancel?(e: PointerEvent): void;
45
+ onMouseDown?(e: MouseEvent): void;
46
+ onMouseUp?(e: MouseEvent): void;
47
+ onTouchStart?(e: TouchEvent): void;
48
+ onTouchEnd?(e: TouchEvent): void;
49
+ onTouchCancel?(e: TouchEvent): void;
50
+ onSubmit?(e: SubmitEvent): void;
51
+ }
52
+
53
+ const CommonDialogDivs = Object.values(CommonUIModule).filter(
54
+ (m: any) => typeof m === 'object' && m?.render?.toString().includes('"div",Object.assign({},'),
55
+ );
56
+ const MappedDialogDivs = new Map(
57
+ Object.values(CommonDialogDivs).map((m: any) => {
58
+ const renderedDiv = m.render({});
59
+ // Take only the first class name segment as it identifies the element we want
60
+ return [renderedDiv.props.className.split(' ')[0], m];
61
+ }),
62
+ );
63
+
64
+ export const DialogHeader = MappedDialogDivs.get('DialogHeader') as FC<DialogCommonProps>;
65
+ export const DialogSubHeader = MappedDialogDivs.get('DialogSubHeader') as FC<DialogCommonProps>;
66
+ export const DialogFooter = MappedDialogDivs.get('DialogFooter') as FC<DialogCommonProps>;
67
+ export const DialogLabel = MappedDialogDivs.get('DialogLabel') as FC<DialogCommonProps>;
68
+ export const DialogBodyText = MappedDialogDivs.get('DialogBodyText') as FC<DialogCommonProps>;
69
+ export const DialogBody = MappedDialogDivs.get('DialogBody') as FC<DialogCommonProps>;
70
+ export const DialogControlsSection = MappedDialogDivs.get('DialogControlsSection') as FC<DialogCommonProps>;
71
+ export const DialogControlsSectionHeader = MappedDialogDivs.get('DialogControlsSectionHeader') as FC<DialogCommonProps>;
72
+
73
+ export const DialogButtonPrimary = Object.values(CommonUIModule).find(
74
+ (mod: any) => mod?.render?.toString()?.includes('DialogButton') && mod?.render?.toString()?.includes('Primary'),
75
+ ) as FC<DialogButtonProps>;
76
+
77
+ export const DialogButtonSecondary = Object.values(CommonUIModule).find(
78
+ (mod: any) =>
79
+ mod?.render?.toString()?.includes('Object.assign({type:"button"') &&
80
+ mod?.render?.toString()?.includes('DialogButton') &&
81
+ mod?.render?.toString()?.includes('Secondary'),
82
+ ) as FC<DialogButtonProps>;
83
+
84
+ // This is the "main" button. The Primary can act as a submit button,
85
+ // therefore secondary is chosen (also for backwards comp. reasons)
86
+ export const DialogButton = DialogButtonSecondary;
@@ -0,0 +1,36 @@
1
+ import { FC, ReactNode } from 'react';
2
+
3
+ import { findModule } from '../webpack';
4
+ import { DialogCommonProps } from './Dialog';
5
+ import { FooterLegendProps } from './FooterLegend';
6
+
7
+ export interface DialogCheckboxProps extends DialogCommonProps, FooterLegendProps {
8
+ onChange?(checked: boolean): void;
9
+ label?: ReactNode;
10
+ description?: ReactNode;
11
+ disabled?: boolean;
12
+ tooltip?: string;
13
+ color?: string;
14
+ highlightColor?: string;
15
+ bottomSeparator?: 'standard' | 'thick' | 'none';
16
+ controlled?: boolean;
17
+ checked?: boolean;
18
+ onClick?(evt: Event): void;
19
+ }
20
+
21
+ export const DialogCheckbox = Object.values(
22
+ findModule((m: any) => {
23
+ if (typeof m !== 'object') return false;
24
+ for (const prop in m) {
25
+ if (m[prop]?.prototype?.GetPanelElementProps) return true;
26
+ }
27
+ return false;
28
+ }),
29
+ ).find(
30
+ (m: any) =>
31
+ m.contextType &&
32
+ m.prototype?.render.toString().includes('fallback:') &&
33
+ m?.prototype?.SetChecked &&
34
+ m?.prototype?.Toggle &&
35
+ m?.prototype?.GetPanelElementProps,
36
+ ) as FC<DialogCheckboxProps>;
@@ -0,0 +1,49 @@
1
+ import { ReactNode, VFC } from 'react';
2
+
3
+ import { CommonUIModule } from '../webpack';
4
+ import { ItemProps } from './Item';
5
+
6
+ export interface SingleDropdownOption {
7
+ data: any;
8
+ label: ReactNode;
9
+
10
+ options?: never;
11
+ }
12
+
13
+ export interface MultiDropdownOption {
14
+ label: ReactNode;
15
+ options: DropdownOption[];
16
+
17
+ data?: never;
18
+ }
19
+
20
+ export type DropdownOption = SingleDropdownOption | MultiDropdownOption;
21
+
22
+ export interface DropdownMenuPositionOptions {
23
+ [_: string]: unknown
24
+ bMatchWidth?: boolean
25
+ }
26
+
27
+ export interface DropdownProps {
28
+ rgOptions: DropdownOption[];
29
+ selectedOption: any;
30
+ disabled?: boolean;
31
+ onMenuWillOpen?(showMenu: () => void): void;
32
+ onMenuOpened?(): void;
33
+ onChange?(data: SingleDropdownOption): void;
34
+ contextMenuPositionOptions?: DropdownMenuPositionOptions;
35
+ menuLabel?: string;
36
+ strDefaultLabel?: string;
37
+ renderButtonValue?(element: ReactNode): ReactNode;
38
+ focusable?: boolean;
39
+ }
40
+
41
+ export const Dropdown = Object.values(CommonUIModule).find(
42
+ (mod: any) => mod?.prototype?.SetSelectedOption && mod?.prototype?.BuildMenu,
43
+ ) as VFC<DropdownProps>;
44
+
45
+ export interface DropdownItemProps extends DropdownProps, ItemProps {}
46
+
47
+ export const DropdownItem = Object.values(CommonUIModule).find((mod: any) =>
48
+ mod?.toString()?.includes('"dropDownControlRef","description"'),
49
+ ) as VFC<DropdownItemProps>;
@@ -0,0 +1,28 @@
1
+ import { FC, ReactNode, RefAttributes } from 'react';
2
+
3
+ import { Export, findModuleExport } from '../webpack';
4
+ import { FooterLegendProps } from './FooterLegend';
5
+
6
+ export interface FieldProps extends FooterLegendProps {
7
+ label?: ReactNode;
8
+ bottomSeparator?: 'standard' | 'thick' | 'none';
9
+ description?: ReactNode;
10
+ disabled?: boolean;
11
+ icon?: ReactNode;
12
+ inlineWrap?: 'keep-inline' | 'shift-children-below'; // If label is too long it will move shildren below before starting to wrap label
13
+ childrenLayout?: 'below' | 'inline';
14
+ childrenContainerWidth?: 'min' | 'max' | 'fixed'; // Does not work with childrenLayout==='below'
15
+ spacingBetweenLabelAndChild?: 'none'; // This applies only when childrenLayout==='below'
16
+ padding?: 'none' | 'standard' | 'compact';
17
+ className?: string;
18
+ highlightOnFocus?: boolean;
19
+ indentLevel?: number;
20
+ verticalAlignment?: 'center' | 'none'; // Alligns inline label with children
21
+ focusable?: boolean; // Allows to get focus without any focusable children or on* callbacks
22
+ onActivate?: (e: CustomEvent | MouseEvent) => void;
23
+ onClick?: (e: CustomEvent | MouseEvent) => void;
24
+ }
25
+
26
+ export const Field = findModuleExport((e: Export) => e?.render?.toString().includes('"shift-children-below"')) as FC<
27
+ FieldProps & RefAttributes<HTMLDivElement>
28
+ >;
@@ -0,0 +1,15 @@
1
+ import { ElementType, FC, ReactNode } from 'react';
2
+
3
+ import { Export, findModuleExport } from '../webpack';
4
+
5
+ export interface FocusRingProps {
6
+ className?: string;
7
+ rootClassName?: string;
8
+ render?: ElementType;
9
+ children?: ReactNode;
10
+ NavigationManager?: any;
11
+ }
12
+
13
+ export const FocusRing = findModuleExport((e: Export) =>
14
+ e?.toString()?.includes('.GetShowDebugFocusRing())'),
15
+ ) as FC<FocusRingProps>;
@@ -0,0 +1,18 @@
1
+ import { HTMLAttributes, ReactNode, RefAttributes, VFC } from 'react';
2
+
3
+ import { Export, findModuleExport } from '../webpack';
4
+ import { FooterLegendProps } from './FooterLegend';
5
+
6
+ export interface FocusableProps extends HTMLAttributes<HTMLDivElement>, FooterLegendProps {
7
+ children: ReactNode;
8
+ 'flow-children'?: string;
9
+ focusClassName?: string;
10
+ focusWithinClassName?: string;
11
+ noFocusRing?: boolean;
12
+ onActivate?: (e: CustomEvent) => void;
13
+ onCancel?: (e: CustomEvent) => void;
14
+ }
15
+
16
+ export const Focusable = findModuleExport((e: Export) =>
17
+ e?.render?.toString()?.includes('["flow-children","onActivate","onCancel","focusClassName",'),
18
+ ) as VFC<FocusableProps & RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,67 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ export enum GamepadButton {
4
+ INVALID,
5
+ OK,
6
+ CANCEL,
7
+ SECONDARY,
8
+ OPTIONS,
9
+ BUMPER_LEFT,
10
+ BUMPER_RIGHT,
11
+ TRIGGER_LEFT,
12
+ TRIGGER_RIGHT,
13
+ DIR_UP,
14
+ DIR_DOWN,
15
+ DIR_LEFT,
16
+ DIR_RIGHT,
17
+ SELECT,
18
+ START,
19
+ LSTICK_CLICK,
20
+ RSTICK_CLICK,
21
+ LSTICK_TOUCH,
22
+ RSTICK_TOUCH,
23
+ LPAD_TOUCH,
24
+ LPAD_CLICK,
25
+ RPAD_TOUCH,
26
+ RPAD_CLICK,
27
+ REAR_LEFT_UPPER,
28
+ REAR_LEFT_LOWER,
29
+ REAR_RIGHT_UPPER,
30
+ REAR_RIGHT_LOWER,
31
+ STEAM_GUIDE,
32
+ STEAM_QUICK_MENU,
33
+ }
34
+ export declare enum NavEntryPositionPreferences {
35
+ FIRST,
36
+ LAST,
37
+ MAINTAIN_X,
38
+ MAINTAIN_Y,
39
+ PREFERRED_CHILD,
40
+ }
41
+ export interface GamepadEventDetail {
42
+ button: number;
43
+ is_repeat?: boolean;
44
+ source: number;
45
+ }
46
+ export declare type ActionDescriptionMap = {
47
+ [key in GamepadButton]?: ReactNode;
48
+ };
49
+ export declare type GamepadEvent = CustomEvent<GamepadEventDetail>;
50
+ export interface FooterLegendProps {
51
+ actionDescriptionMap?: ActionDescriptionMap;
52
+ onOKActionDescription?: ReactNode;
53
+ onCancelActionDescription?: ReactNode;
54
+ onSecondaryActionDescription?: ReactNode;
55
+ onOptionsActionDescription?: ReactNode;
56
+ onMenuActionDescription?: ReactNode;
57
+ onButtonDown?: (evt: GamepadEvent) => void;
58
+ onButtonUp?: (evt: GamepadEvent) => void;
59
+ onOKButton?: (evt: GamepadEvent) => void;
60
+ onCancelButton?: (evt: GamepadEvent) => void;
61
+ onSecondaryButton?: (evt: GamepadEvent) => void;
62
+ onOptionsButton?: (evt: GamepadEvent) => void;
63
+ onGamepadDirection?: (evt: GamepadEvent) => void;
64
+ onGamepadFocus?: (evt: GamepadEvent) => void;
65
+ onGamepadBlur?: (evt: GamepadEvent) => void;
66
+ onMenuButton?: (evt: GamepadEvent) => void;
67
+ }
@@ -0,0 +1,12 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ export interface ItemProps {
4
+ label?: ReactNode;
5
+ description?: ReactNode;
6
+ layout?: 'below' | 'inline';
7
+ icon?: ReactNode;
8
+ bottomSeparator?: 'standard' | 'thick' | 'none';
9
+ indentLevel?: number;
10
+ tooltip?: string;
11
+ highlightOnFocus?: boolean;
12
+ }
@@ -0,0 +1,20 @@
1
+ import { CSSProperties, FC } from 'react';
2
+
3
+ import { Export, findModuleExport } from '../webpack';
4
+
5
+ export interface MarqueeProps {
6
+ play?: boolean;
7
+ direction?: 'left' | 'right';
8
+ speed?: number;
9
+ delay?: number;
10
+ fadeLength?: number;
11
+ center?: boolean;
12
+ resetOnPause?: boolean;
13
+ style?: CSSProperties;
14
+ className?: string;
15
+ children: React.ReactNode;
16
+ }
17
+
18
+ export const Marquee: FC<MarqueeProps> = findModuleExport(
19
+ (e: Export) => e?.toString && e.toString().includes('.Marquee') && e.toString().includes('--fade-length'),
20
+ );
@@ -0,0 +1,57 @@
1
+ import { FC, ReactNode } from 'react';
2
+
3
+ import { fakeRenderComponent } from '../utils';
4
+ import { Export, findModuleExport } from '../webpack';
5
+ import { FooterLegendProps } from './FooterLegend';
6
+
7
+ export const showContextMenu: (children: ReactNode, parent?: EventTarget) => void = findModuleExport(
8
+ (e: Export) => typeof e === 'function' && e.toString().includes('stopPropagation))'),
9
+ );
10
+
11
+ export interface MenuProps extends FooterLegendProps {
12
+ label: string;
13
+ onCancel?(): void;
14
+ cancelText?: string;
15
+ children?: ReactNode;
16
+ }
17
+
18
+ export const Menu: FC<MenuProps> = findModuleExport(
19
+ (e: Export) => e?.prototype?.HideIfSubmenu && e?.prototype?.HideMenu,
20
+ );
21
+
22
+ export interface MenuGroupProps {
23
+ label: string;
24
+ disabled?: boolean;
25
+ children?: ReactNode;
26
+ }
27
+
28
+ export const MenuGroup: FC<MenuGroupProps> = findModuleExport(
29
+ (e: Export) =>
30
+ (e?.toString()?.includes?.('bInGamepadUI:') &&
31
+ fakeRenderComponent(() => e({ overview: { appid: 7 } }))?.type?.prototype?.RenderSubMenu) ||
32
+ (e?.prototype?.RenderSubMenu && e?.prototype?.ShowSubMenu),
33
+ );
34
+ export interface MenuItemProps extends FooterLegendProps {
35
+ bInteractableItem?: boolean;
36
+ onClick?(evt: Event): void;
37
+ onSelected?(evt: Event): void;
38
+ onMouseEnter?(evt: MouseEvent): void;
39
+ onMoveRight?(): void;
40
+ selected?: boolean;
41
+ disabled?: boolean;
42
+ bPlayAudio?: boolean;
43
+ tone?: 'positive' | 'emphasis' | 'destructive';
44
+ children?: ReactNode;
45
+ }
46
+
47
+ export const MenuItem: FC<MenuItemProps> = findModuleExport(
48
+ (e: Export) =>
49
+ e?.render?.toString()?.includes('bPlayAudio:') || (e?.prototype?.OnOKButton && e?.prototype?.OnMouseEnter),
50
+ );
51
+
52
+ /*
53
+ all().map(m => {
54
+ if (typeof m !== "object") return undefined;
55
+ for (let prop in m) { if (m[prop]?.prototype?.OK && m[prop]?.prototype?.Cancel && m[prop]?.prototype?.render) return m[prop]}
56
+ }).find(x => x)
57
+ */