Compare commits

...

37 Commits

Author SHA1 Message Date
semantic-release-bot
e5120928d3 chore(release): 3.5.0 [CI SKIP] 2022-10-08 02:04:42 +00:00
AAGaming
abbd3cddae feat(Tabs): initial tabs component, props, docs 2022-10-07 22:04:02 -04:00
semantic-release-bot
621e47c6a0 chore(release): 3.4.0 [CI SKIP] 2022-10-06 01:43:25 +00:00
OMGDuke
e2920dd91e feat(hooks): Added useParams hook (#36) 2022-10-05 21:42:54 -04:00
semantic-release-bot
67a76e2691 chore(release): 3.3.5 [CI SKIP] 2022-10-02 16:38:31 +00:00
AAGaming
0f205e8916 fix(docs): set categorizeByGroup to true 2022-10-02 12:37:45 -04:00
semantic-release-bot
472307e4a4 chore(release): 3.3.4 [CI SKIP] 2022-10-02 16:34:00 +00:00
AAGaming
fbd936dc1f fix(docs): build each component as a seperate page 2022-10-02 12:33:20 -04:00
AAGaming
33dd4e5548 chore(docs): initial typedoc setup (#35) 2022-10-02 12:22:58 -04:00
semantic-release-bot
4b76ccd91a chore(release): 3.3.3 [CI SKIP] 2022-10-02 12:36:26 +00:00
Lukas Senionis
99ad7543a9 fix(modal): make children optional (#34) 2022-10-02 08:36:00 -04:00
semantic-release-bot
b5dc08a977 chore(release): 3.3.2 [CI SKIP] 2022-10-02 02:31:09 +00:00
AAGaming
40871af853 fix(modal): allow children 2022-10-01 22:30:33 -04:00
Lukas Senionis
c910dbde79 Add dialog components (#22) 2022-10-01 21:41:47 -04:00
Lukas Senionis
4cbb30c21c Add custom-hook: useQuickAccessVisible (#28) 2022-10-01 21:38:46 -04:00
semantic-release-bot
54a1ef6201 chore(release): 3.3.1 [CI SKIP] 2022-10-02 01:33:33 +00:00
Barend Du Toit
ed0be5e87e fix(SidebarNavigation): add more props (#29) 2022-10-01 21:33:02 -04:00
Travis Lane
a064163b49 updated static-classes (#26) 2022-10-01 21:32:23 -04:00
semantic-release-bot
0b4fcb8d49 chore(release): 3.3.0 [CI SKIP] 2022-10-02 01:31:47 +00:00
Barend Du Toit
4233128c7e feat(Menu): add nested menu groups + more props (#30) 2022-10-01 21:31:18 -04:00
TrainDoctor
43cb2726d8 chore(license): remove un-needed exceptions 2022-09-30 21:50:23 -07:00
TrainDoctor
5d9c506fe7 Update package.json 2022-09-30 21:48:33 -07:00
semantic-release-bot
35a061759a chore(release): 3.2.2 [CI SKIP] 2022-09-29 16:36:04 +00:00
Lukas Senionis
1fbe55aa54 fix(modal): extend props for modals (#32) 2022-09-29 12:34:00 -04:00
semantic-release-bot
66eb0cbbf3 chore(release): 3.2.1 [CI SKIP] 2022-09-24 17:49:57 +00:00
Lukas Senionis
6996e5424f fix(modal): update showModal types (#27) 2022-09-24 13:49:23 -04:00
Travis Lane
ad643836f0 updated DialogButton props and added nav pref enum (#25) 2022-09-22 17:50:07 -04:00
semantic-release-bot
b39ba26b28 chore(release): 3.2.0 [CI SKIP] 2022-09-20 21:37:13 +00:00
AAGaming
130dfa24c5 feat(FooterLegend): add GamepadEvent 2022-09-20 17:36:16 -04:00
Martmists
71babc82c8 Add ColorPickerModal to exports (#24) 2022-09-20 08:46:37 -04:00
semantic-release-bot
bb440deed6 chore(release): 3.1.4 [CI SKIP] 2022-09-19 21:33:33 +00:00
TrainDoctor
064c161b67 fix(License): update license in package.json 2022-09-19 14:32:37 -07:00
TrainDoctor
6abf0efc10 Update package.json 2022-09-19 14:30:08 -07:00
TrainDoctor
537b8301ec Add license exception for static linking 2022-09-18 14:31:18 -07:00
Beebles
2ae87da37a Add ColorPickerModal component (with issues fixed) to custom-components (#21) 2022-09-18 17:03:00 -04:00
semantic-release-bot
0d248f3987 chore(release): 3.1.3 [CI SKIP] 2022-09-18 19:38:35 +00:00
AAGaming
74aefc7b3c fix(DialogButton): fix ref type 2022-09-18 15:37:46 -04:00
23 changed files with 924 additions and 54 deletions

53
.github/workflows/docs.yaml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Generate docs
on:
push:
branches:
- main
jobs:
release:
name: Generate Docs
runs-on: ubuntu-22.04
steps:
- name: Setup | Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
path: lib
- name: Setup | Checkout wiki
uses: actions/checkout@v3
with:
repository: SteamDeckHomebrew/wiki
path: wiki
ssh-key: ${{ secrets.SSH_DEPLOY_KEY }}
persist-credentials: true
- name: Setup | Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup | Dependencies
run: |
cd lib
npm ci
- name: Build Docs
run: |
cd lib
npm run docs -- --out ../wiki/api-docs/decky-frontend-lib
- name: Commit files
run: |
cd wiki
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add -A ./api-docs/decky-frontend-lib
git commit -m "Update decky-frontend-lib API docs"
- name: Push changes
uses: ad-m/github-push-action@master
with:
ssh: true
directory: ./wiki
repository: SteamDeckHomebrew/wiki
branch: main

2
.gitignore vendored
View File

@@ -38,3 +38,5 @@ research/
# PNPM lockfile # PNPM lockfile
pnpm-lock.yaml pnpm-lock.yaml
docs/

View File

@@ -1,3 +1,94 @@
# [3.5.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.4.0...v3.5.0) (2022-10-08)
### Features
* **Tabs:** initial tabs component, props, docs ([abbd3cd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/abbd3cddae24039cbc9b7d955924431e8fbacf94))
# [3.4.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.5...v3.4.0) (2022-10-06)
### Features
* **hooks:** Added useParams hook ([#36](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/36)) ([e2920dd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e2920dd91e81d915a2319280d8473df71a4e4232))
## [3.3.5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.4...v3.3.5) (2022-10-02)
### Bug Fixes
* **docs:** set categorizeByGroup to true ([0f205e8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/0f205e891694e2cee211b0c2db74a6dda2432507))
## [3.3.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.3...v3.3.4) (2022-10-02)
### Bug Fixes
* **docs:** build each component as a seperate page ([fbd936d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/fbd936dc1fe4c23c72f4ee27af95abc004382acd))
## [3.3.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.2...v3.3.3) (2022-10-02)
### Bug Fixes
* **modal:** make children optional ([#34](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/34)) ([99ad754](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/99ad7543a9966b8ff3f4ec01e6f05c94e5242c93))
## [3.3.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.1...v3.3.2) (2022-10-02)
### Bug Fixes
* **modal:** allow children ([40871af](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/40871af8539858f435c83123a56d4b31b63d627d))
## [3.3.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.0...v3.3.1) (2022-10-02)
### Bug Fixes
* **SidebarNavigation:** add more props ([#29](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/29)) ([ed0be5e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/ed0be5e87e964ed57cc99b40ff55fe35a2f518b2))
# [3.3.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.2.2...v3.3.0) (2022-10-02)
### Features
* **Menu:** add nested menu groups + more props ([#30](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/30)) ([4233128](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/4233128c7ee8c6e5ab4ee74385c7b1b911d507a6))
## [3.2.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.2.1...v3.2.2) (2022-09-29)
### Bug Fixes
* **modal:** extend props for modals ([#32](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/32)) ([1fbe55a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/1fbe55aa544c9e84e2b3e2d6af9950db2fe7546c))
## [3.2.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.2.0...v3.2.1) (2022-09-24)
### Bug Fixes
* **modal:** update showModal types ([#27](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/27)) ([6996e54](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/6996e5424f33467ef5bb93f47614058c127cb3ee))
# [3.2.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.1.4...v3.2.0) (2022-09-20)
### Features
* **FooterLegend:** add GamepadEvent ([130dfa2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/130dfa24c51c3a670cca9ebc38e4891618532bef))
## [3.1.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.1.3...v3.1.4) (2022-09-19)
### Bug Fixes
* **License:** update license in package.json ([064c161](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/064c161b6736bb5574f28cb986c5899620fd69fe))
## [3.1.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.1.2...v3.1.3) (2022-09-18)
### Bug Fixes
* **DialogButton:** fix ref type ([74aefc7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/74aefc7b3c19a98fb607e78c4063c098a2e12546))
## [3.1.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.1.1...v3.1.2) (2022-09-18) ## [3.1.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.1.1...v3.1.2) (2022-09-18)

View File

@@ -1,4 +1,4 @@
GNU LESSER GENERAL PUBLIC LICENSE GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999 Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc. Copyright (C) 1991, 1999 Free Software Foundation, Inc.

219
package-lock.json generated
View File

@@ -1,13 +1,13 @@
{ {
"name": "decky-frontend-lib", "name": "decky-frontend-lib",
"version": "3.1.2", "version": "3.5.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "decky-frontend-lib", "name": "decky-frontend-lib",
"version": "3.1.2", "version": "3.5.0",
"license": "GPL-2.0-or-later", "license": "LGPL-2.1",
"dependencies": { "dependencies": {
"minimist": "^1.2.6" "minimist": "^1.2.6"
}, },
@@ -28,6 +28,10 @@
"semantic-release": "^19.0.3", "semantic-release": "^19.0.3",
"shx": "^0.3.4", "shx": "^0.3.4",
"ts-jest": "^27.1.4", "ts-jest": "^27.1.4",
"typedoc": "^0.23.15",
"typedoc-plugin-markdown": "^3.13.6",
"typedoc-plugin-mdn-links": "^2.0.0",
"typedoc-plugin-missing-exports": "^1.0.0",
"typescript": "^4.6.3" "typescript": "^4.6.3"
} }
}, },
@@ -7335,6 +7339,12 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/jsonc-parser": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
"dev": true
},
"node_modules/jsonfile": { "node_modules/jsonfile": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -7624,6 +7634,12 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/lunr": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
"dev": true
},
"node_modules/make-dir": { "node_modules/make-dir": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -7667,9 +7683,9 @@
} }
}, },
"node_modules/marked": { "node_modules/marked": {
"version": "4.0.16", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.16.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz",
"integrity": "sha512-wahonIQ5Jnyatt2fn8KqF/nIqZM8mh3oRu2+l5EANGMhu6RFjiSG52QNE2eWzFMI94HqYSgN184NurgNG6CztA==", "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==",
"dev": true, "dev": true,
"bin": { "bin": {
"marked": "bin/marked.js" "marked": "bin/marked.js"
@@ -11706,6 +11722,17 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/shiki": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz",
"integrity": "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==",
"dev": true,
"dependencies": {
"jsonc-parser": "^3.0.0",
"vscode-oniguruma": "^1.6.1",
"vscode-textmate": "^6.0.0"
}
},
"node_modules/shx": { "node_modules/shx": {
"version": "0.3.4", "version": "0.3.4",
"resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz",
@@ -12401,6 +12428,78 @@
"is-typedarray": "^1.0.0" "is-typedarray": "^1.0.0"
} }
}, },
"node_modules/typedoc": {
"version": "0.23.15",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.15.tgz",
"integrity": "sha512-x9Zu+tTnwxb9YdVr+zvX7LYzyBl1nieOr6lrSHbHsA22/RJK2m4Y525WIg5Mj4jWCmfL47v6f4hUzY7EIuwS5w==",
"dev": true,
"dependencies": {
"lunr": "^2.3.9",
"marked": "^4.0.19",
"minimatch": "^5.1.0",
"shiki": "^0.11.1"
},
"bin": {
"typedoc": "bin/typedoc"
},
"engines": {
"node": ">= 14.14"
},
"peerDependencies": {
"typescript": "4.6.x || 4.7.x || 4.8.x"
}
},
"node_modules/typedoc-plugin-markdown": {
"version": "3.13.6",
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.13.6.tgz",
"integrity": "sha512-ISSc9v3BK7HkokxSBuJPttXox4tJ6hP0N9wfSIk0fmLN67+eqtAxbk97gs2nDiuha+RTO5eW9gdeAb+RPP0mgg==",
"dev": true,
"dependencies": {
"handlebars": "^4.7.7"
},
"peerDependencies": {
"typedoc": ">=0.23.0"
}
},
"node_modules/typedoc-plugin-mdn-links": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/typedoc-plugin-mdn-links/-/typedoc-plugin-mdn-links-2.0.0.tgz",
"integrity": "sha512-IGLuelXPOenGdmklr5DHgPPf/MfZj7aEYCxCMtPN8C1D0lA7w0YLahd0jhDDcOMU7zL1EPcM5pPnhZHltDhqGQ==",
"dev": true,
"peerDependencies": {
"typedoc": "0.22.x || 0.23.x"
}
},
"node_modules/typedoc-plugin-missing-exports": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-1.0.0.tgz",
"integrity": "sha512-7s6znXnuAj1eD9KYPyzVzR1lBF5nwAY8IKccP5sdoO9crG4lpd16RoFpLsh2PccJM+I2NASpr0+/NMka6ThwVA==",
"dev": true,
"peerDependencies": {
"typedoc": "0.22.x || 0.23.x"
}
},
"node_modules/typedoc/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/typedoc/node_modules/minimatch": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
"integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/typescript": { "node_modules/typescript": {
"version": "4.7.3", "version": "4.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
@@ -12514,6 +12613,18 @@
"spdx-expression-parse": "^3.0.0" "spdx-expression-parse": "^3.0.0"
} }
}, },
"node_modules/vscode-oniguruma": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz",
"integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==",
"dev": true
},
"node_modules/vscode-textmate": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-6.0.0.tgz",
"integrity": "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==",
"dev": true
},
"node_modules/w3c-hr-time": { "node_modules/w3c-hr-time": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
@@ -18354,6 +18465,12 @@
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"dev": true "dev": true
}, },
"jsonc-parser": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
"dev": true
},
"jsonfile": { "jsonfile": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -18581,6 +18698,12 @@
"yallist": "^4.0.0" "yallist": "^4.0.0"
} }
}, },
"lunr": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
"dev": true
},
"make-dir": { "make-dir": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -18612,9 +18735,9 @@
"dev": true "dev": true
}, },
"marked": { "marked": {
"version": "4.0.16", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.16.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz",
"integrity": "sha512-wahonIQ5Jnyatt2fn8KqF/nIqZM8mh3oRu2+l5EANGMhu6RFjiSG52QNE2eWzFMI94HqYSgN184NurgNG6CztA==", "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==",
"dev": true "dev": true
}, },
"marked-terminal": { "marked-terminal": {
@@ -21487,6 +21610,17 @@
"rechoir": "^0.6.2" "rechoir": "^0.6.2"
} }
}, },
"shiki": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz",
"integrity": "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==",
"dev": true,
"requires": {
"jsonc-parser": "^3.0.0",
"vscode-oniguruma": "^1.6.1",
"vscode-textmate": "^6.0.0"
}
},
"shx": { "shx": {
"version": "0.3.4", "version": "0.3.4",
"resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz",
@@ -22008,6 +22142,61 @@
"is-typedarray": "^1.0.0" "is-typedarray": "^1.0.0"
} }
}, },
"typedoc": {
"version": "0.23.15",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.15.tgz",
"integrity": "sha512-x9Zu+tTnwxb9YdVr+zvX7LYzyBl1nieOr6lrSHbHsA22/RJK2m4Y525WIg5Mj4jWCmfL47v6f4hUzY7EIuwS5w==",
"dev": true,
"requires": {
"lunr": "^2.3.9",
"marked": "^4.0.19",
"minimatch": "^5.1.0",
"shiki": "^0.11.1"
},
"dependencies": {
"brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0"
}
},
"minimatch": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
"integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
"dev": true,
"requires": {
"brace-expansion": "^2.0.1"
}
}
}
},
"typedoc-plugin-markdown": {
"version": "3.13.6",
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.13.6.tgz",
"integrity": "sha512-ISSc9v3BK7HkokxSBuJPttXox4tJ6hP0N9wfSIk0fmLN67+eqtAxbk97gs2nDiuha+RTO5eW9gdeAb+RPP0mgg==",
"dev": true,
"requires": {
"handlebars": "^4.7.7"
}
},
"typedoc-plugin-mdn-links": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/typedoc-plugin-mdn-links/-/typedoc-plugin-mdn-links-2.0.0.tgz",
"integrity": "sha512-IGLuelXPOenGdmklr5DHgPPf/MfZj7aEYCxCMtPN8C1D0lA7w0YLahd0jhDDcOMU7zL1EPcM5pPnhZHltDhqGQ==",
"dev": true,
"requires": {}
},
"typedoc-plugin-missing-exports": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-1.0.0.tgz",
"integrity": "sha512-7s6znXnuAj1eD9KYPyzVzR1lBF5nwAY8IKccP5sdoO9crG4lpd16RoFpLsh2PccJM+I2NASpr0+/NMka6ThwVA==",
"dev": true,
"requires": {}
},
"typescript": { "typescript": {
"version": "4.7.3", "version": "4.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
@@ -22098,6 +22287,18 @@
"spdx-expression-parse": "^3.0.0" "spdx-expression-parse": "^3.0.0"
} }
}, },
"vscode-oniguruma": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz",
"integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==",
"dev": true
},
"vscode-textmate": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-6.0.0.tgz",
"integrity": "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==",
"dev": true
},
"w3c-hr-time": { "w3c-hr-time": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",

View File

@@ -1,6 +1,6 @@
{ {
"name": "decky-frontend-lib", "name": "decky-frontend-lib",
"version": "3.1.2", "version": "3.5.0",
"description": "A library for building decky plugins", "description": "A library for building decky plugins",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
@@ -9,6 +9,7 @@
"scripts": { "scripts": {
"build": "shx rm -rf dist && tsc -b", "build": "shx rm -rf dist && tsc -b",
"dev": "tsc -b -w", "dev": "tsc -b -w",
"docs": "typedoc --tsconfig ./tsconfig.json src/**/*",
"prepack": "npm run build", "prepack": "npm run build",
"test": "echo 'No tests for now!'", "test": "echo 'No tests for now!'",
"prepare": "husky install", "prepare": "husky install",
@@ -30,7 +31,7 @@
"components" "components"
], ],
"author": "Jonas Dellinger <jonas@dellinger.dev>", "author": "Jonas Dellinger <jonas@dellinger.dev>",
"license": "GPL-2.0-or-later", "license": "LGPL-2.1",
"bugs": { "bugs": {
"url": "https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues" "url": "https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues"
}, },
@@ -57,6 +58,10 @@
"semantic-release": "^19.0.3", "semantic-release": "^19.0.3",
"shx": "^0.3.4", "shx": "^0.3.4",
"ts-jest": "^27.1.4", "ts-jest": "^27.1.4",
"typedoc": "^0.23.15",
"typedoc-plugin-markdown": "^3.13.6",
"typedoc-plugin-mdn-links": "^2.0.0",
"typedoc-plugin-missing-exports": "^1.0.0",
"typescript": "^4.6.3" "typescript": "^4.6.3"
}, },
"pnpm": { "pnpm": {

View File

@@ -0,0 +1,163 @@
import { gamepadSliderClasses, ConfirmModal, SliderField } from "../deck-components";
import { useState, FC, CSSProperties } from "react";
interface ColorPickerModalProps {
closeModal: () => void;
onConfirm?(HSLString: string): any;
title?: string;
defaultH?: number;
defaultS?: number;
defaultL?: number;
defaultA?: number;
}
export const ColorPickerModal: FC<ColorPickerModalProps> = ({
closeModal,
onConfirm = () => {},
title = "Color Picker",
defaultH = 0,
defaultS = 100,
defaultL = 50,
defaultA = 1,
}) => {
const [H, setH] = useState<number>(defaultH);
const [S, setS] = useState<number>(defaultS);
const [L, setL] = useState<number>(defaultL);
const [A, setA] = useState<number>(defaultA);
const colorPickerCSSVars = {
"--decky-color-picker-hvalue": `${H}`,
"--decky-color-picker-svalue": `${S}%`,
"--decky-color-picker-lvalue": `${L}%`,
"--decky-color-picker-avalue": `${A}`,
} as CSSProperties;
return (
<ConfirmModal
bAllowFullSize
onCancel={closeModal}
onOK={() => {
onConfirm(`hsla(${H}, ${S}%, ${L}%, ${A})`);
closeModal();
}}
>
<style>
{`
/* This removes the cyan track color that is behind the slider head */
.ColorPicker_Container .${gamepadSliderClasses.SliderTrack} {
--left-track-color: #0000;
/* This is for compatibility with the "Colored Toggles" CSSLoader Theme*/
--colored-toggles-main-color: #0000;
}
.ColorPicker_HSlider .${gamepadSliderClasses.SliderTrack} {
background: linear-gradient(
270deg,
hsla(360, var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue)),
hsla(270, var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue)),
hsla(180, var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue)),
hsla(90, var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue)),
hsla(0, var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue))
);
}
.ColorPicker_SSlider .${gamepadSliderClasses.SliderTrack} {
background: linear-gradient(
90deg,
hsla(var(--decky-color-picker-hvalue), 0%, var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue)),
hsla(var(--decky-color-picker-hvalue), 100%, var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue))
);
}
.ColorPicker_LSlider .${gamepadSliderClasses.SliderTrack} {
background: linear-gradient(
90deg,
hsla(var(--decky-color-picker-hvalue), var(--decky-color-picker-svalue), 0%, var(--decky-color-picker-avalue)),
hsla(var(--decky-color-picker-hvalue), var(--decky-color-picker-svalue), 50%, var(--decky-color-picker-avalue)),
hsla(var(--decky-color-picker-hvalue), var(--decky-color-picker-svalue), 100%, var(--decky-color-picker-avalue))
);
}
.ColorPicker_ASlider .${gamepadSliderClasses.SliderTrack} {
background: linear-gradient(
90deg,
hsla(var(--decky-color-picker-hvalue), var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), 0),
hsla(var(--decky-color-picker-hvalue), var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), 1)
);
}
`}
</style>
<div
className="ColorPicker_ColorDisplayContainer"
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "1em",
// theres a large header by default on the modal, so this just pushes it up into that unused space
marginTop: "-2.5em",
}}
>
<div>
<span style={{ fontSize: "1.5em" }}>
<b>{title}</b>
</span>
</div>
<div
style={{
backgroundColor: `hsla(${H}, ${S}%, ${L}%, ${A})`,
width: "40px",
height: "40px",
}}
></div>
</div>
<div className="ColorPicker_Container" style={colorPickerCSSVars}>
<div className="ColorPicker_HSlider">
<SliderField
showValue
editableValue
label="Hue"
value={H}
min={0}
max={360}
onChange={setH}
/>
</div>
<div className="ColorPicker_SSlider">
<SliderField
showValue
editableValue
label="Saturation"
value={S}
min={0}
max={100}
onChange={setS}
/>
</div>
<div className="ColorPicker_LSlider">
<SliderField
showValue
editableValue
label="Lightness"
value={L}
min={0}
max={100}
onChange={setL}
/>
</div>
<div className="ColorPicker_ASlider">
<SliderField
showValue
editableValue
label="Alpha"
value={A}
step={0.1}
min={0}
max={1}
onChange={setA}
/>
</div>
</div>
</ConfirmModal>
);
};

View File

@@ -1 +1,2 @@
export * from './SuspensefulImage'; export * from './SuspensefulImage';
export * from './ColorPickerModal';

View File

@@ -0,0 +1 @@
export * from './usequickaccessvisible';

View File

@@ -0,0 +1,67 @@
import { useState, useEffect } from 'react';
declare global {
var FocusNavController: any;
}
/**
* Returns state indicating the visibility of quick access menu.
*
* @remarks
* During development it is possible to open the quick access menu without giving it
* focus in some cases. In such cases, the quick access menu state is invisible.
*
* This seems to be impossible to replicate when running the deck normally. Even in
* the edge cases it always seems to have a focus.
*
* @returns `true` if quick access menu is visible (focused) and `false` otherwise.
*
* @example
* import { VFC, useEffect } from "react";
* import { useQuickAccessVisible } from "decky-frontend-lib";
*
* export const PluginPanelView: VFC<{}> = ({ }) => {
* const isVisible = useQuickAccessVisible();
*
* useEffect(() => {
* if (!isVisible) {
* return;
* }
*
* const interval = setInterval(() => console.log("Hello world!"), 1000);
* return () => {
* clearInterval(interval);
* }
* }, [isVisible])
*
* return (
* <div>
* {isVisible ? "VISIBLE" : "INVISIBLE"}
* </div>
* );
* };
*/
export function useQuickAccessVisible(): boolean {
// Assuming that the component is rendered in QAM already, so true by default...
const [isVisible, setIsVisible] = useState(true);
useEffect(() => {
const quickAccessWindow: Window | null = FocusNavController?.GetGamepadNavTreeByID("QuickAccess-NA")?.m_Root?.m_element?.ownerDocument.defaultView ?? null;
if (quickAccessWindow === null) {
console.error("Could not get window of QuickAccess menu!");
return;
}
const onBlur = () => setIsVisible(false);
const onFocus = () => setIsVisible(true);
quickAccessWindow.addEventListener("blur", onBlur);
quickAccessWindow.addEventListener("focus", onFocus);
return () => {
quickAccessWindow.removeEventListener("blur", onBlur);
quickAccessWindow.removeEventListener("focus", onFocus);
};
}, []);
return isVisible;
}

View File

@@ -1,21 +1,8 @@
import { CSSProperties, FC, RefAttributes } from 'react'; import { FC } from 'react';
import { DialogButton, DialogButtonProps } from "./Dialog";
import { CommonUIModule } from '../webpack'; export interface ButtonProps extends DialogButtonProps {
export interface DialogButtonProps extends RefAttributes<HTMLButtonElement> {
label?: string;
style?: CSSProperties;
className?: string;
noFocusRing?: boolean;
description?: string;
layout?: 'below';
onClick?(e: MouseEvent): void;
disabled?: boolean;
bottomSeparator?: boolean;
} }
export const DialogButton = Object.values(CommonUIModule).find( // Button isn't exported, so call DialogButton to grab it
(mod: any) => export const Button = (DialogButton as any)?.render({}).type as FC<ButtonProps>;
mod?.render?.toString()?.includes('Object.assign({type:"button"') &&
mod?.render?.toString()?.includes('DialogButton'),
) as FC<DialogButtonProps>;

View File

@@ -0,0 +1,63 @@
import { CommonUIModule } from "../webpack";
import { CSSProperties, FC, RefAttributes } from "react";
import { FooterLegendProps } from './FooterLegend';
export interface DialogCommonProps extends RefAttributes<HTMLDivElement> {
style?: CSSProperties;
className?: string;
}
export interface DialogButtonProps extends DialogCommonProps, FooterLegendProps {
noFocusRing?: boolean;
disabled?: boolean;
onClick?(e: MouseEvent): void;
onPointerDown?(e: PointerEvent): void;
onPointerUp?(e: PointerEvent): void;
onPointerCancel?(e: PointerEvent): void;
onMouseDown?(e: MouseEvent): void;
onMouseUp?(e: MouseEvent): void;
onTouchStart?(e: TouchEvent): void;
onTouchEnd?(e: TouchEvent): void;
onTouchCancel?(e: TouchEvent): void;
onSubmit?(e: SubmitEvent): void;
}
const CommonDialogDivs = Object.values(CommonUIModule).filter((m: any) => typeof m === "object" && m?.render?.toString().includes('"div",Object.assign({},'));
const MappedDialogDivs = new Map(Object.values(CommonDialogDivs).map((m: any) => {
const renderedDiv = m.render({});
// Take only the first class name segment as it identifies the element we want
return [renderedDiv.props.className.split(" ")[0], m]
}));
export const DialogHeader = MappedDialogDivs.get("DialogHeader") as FC<DialogCommonProps>;
export const DialogSubHeader = MappedDialogDivs.get("DialogSubHeader") as FC<DialogCommonProps>;
export const DialogFooter = MappedDialogDivs.get("DialogFooter") as FC<DialogCommonProps>;
export const DialogLabel = MappedDialogDivs.get("DialogLabel") as FC<DialogCommonProps>;
export const DialogBodyText = MappedDialogDivs.get("DialogBodyText") as FC<DialogCommonProps>;
export const DialogBody = MappedDialogDivs.get("DialogBody") as FC<DialogCommonProps>;
export const DialogControlsSection = MappedDialogDivs.get("DialogControlsSection") as FC<DialogCommonProps>;
export const DialogControlsSectionHeader = MappedDialogDivs.get("DialogControlsSectionHeader") as FC<DialogCommonProps>;
export const DialogButtonPrimary = Object.values(CommonUIModule).find(
(mod: any) =>
mod?.render?.toString()?.includes('DialogButton') &&
mod?.render?.toString()?.includes('Primary')
) as FC<DialogButtonProps>;
export const DialogButtonSecondary = Object.values(CommonUIModule).find(
(mod: any) =>
mod?.render?.toString()?.includes('Object.assign({type:"button"') &&
mod?.render?.toString()?.includes('DialogButton') &&
mod?.render?.toString()?.includes('Secondary')
) as FC<DialogButtonProps>;
export const DialogButtonSmall = Object.values(CommonUIModule).find(
(mod: any) =>
mod?.render?.toString()?.includes('Object.assign({type:"button"') &&
mod?.render?.toString()?.includes('DialogButton') &&
mod?.render?.toString()?.includes('Small')
) as FC<DialogButtonProps>;
// This is the "main" button. The Primary can act as a submit button,
// therefore secondary is chosen (also for backwards comp. reasons)
export const DialogButton = DialogButtonSecondary;

View File

@@ -1,3 +1,51 @@
export enum GamepadButton {
INVALID,
OK,
CANCEL,
SECONDARY,
OPTIONS,
BUMPER_LEFT,
BUMPER_RIGHT,
TRIGGER_LEFT,
TRIGGER_RIGHT,
DIR_UP,
DIR_DOWN,
DIR_LEFT,
DIR_RIGHT,
SELECT,
START,
LSTICK_CLICK,
RSTICK_CLICK,
LSTICK_TOUCH,
RSTICK_TOUCH,
LPAD_TOUCH,
LPAD_CLICK,
RPAD_TOUCH,
RPAD_CLICK,
REAR_LEFT_UPPER,
REAR_LEFT_LOWER,
REAR_RIGHT_UPPER,
REAR_RIGHT_LOWER,
STEAM_GUIDE,
STEAM_QUICK_MENU
}
export enum NavEntryPositionPreferences {
FIRST,
LAST,
MAINTAIN_X,
MAINTAIN_Y,
PREFERRED_CHILD
}
export interface GamepadEventDetail {
button: number;
is_repeat?: boolean;
source: number;
}
export type GamepadEvent = CustomEvent<GamepadEventDetail>
export interface FooterLegendProps { export interface FooterLegendProps {
actionDescriptionMap?: unknown; actionDescriptionMap?: unknown;
onOKActionDescription?: string; onOKActionDescription?: string;
@@ -5,14 +53,14 @@ export interface FooterLegendProps {
onSecondaryActionDescription?: string; onSecondaryActionDescription?: string;
onOptionsActionDescription?: string; onOptionsActionDescription?: string;
onMenuActionDescription?: string; onMenuActionDescription?: string;
onButtonDown?: () => void; onButtonDown?: (evt: GamepadEvent) => void;
onButtonUp?: () => void; onButtonUp?: (evt: GamepadEvent) => void;
onOKButton?: () => void; onOKButton?: (evt: GamepadEvent) => void;
onCancelButton?: () => void; onCancelButton?: (evt: GamepadEvent) => void;
onSecondaryButton?: () => void; onSecondaryButton?: (evt: GamepadEvent) => void;
onOptionsButton?: () => void; onOptionsButton?: (evt: GamepadEvent) => void;
onGamepadDirection?: () => void; onGamepadDirection?: (evt: GamepadEvent) => void;
onGamepadFocus?: () => void; onGamepadFocus?: (evt: GamepadEvent) => void;
onGamepadBlur?: () => void; onGamepadBlur?: (evt: GamepadEvent) => void;
onMenuButton?: () => void; onMenuButton?: (evt: GamepadEvent) => void;
} }

View File

@@ -15,6 +15,7 @@ export interface MenuProps {
label: string; label: string;
onCancel?(): void; onCancel?(): void;
cancelText?: string; cancelText?: string;
children?: ReactNode;
} }
export const Menu: FC<MenuProps> = findModuleChild((m) => { export const Menu: FC<MenuProps> = findModuleChild((m) => {
@@ -27,8 +28,26 @@ export const Menu: FC<MenuProps> = findModuleChild((m) => {
} }
}); });
export interface MenuGroupProps {
label: string;
disabled?: boolean;
children?: ReactNode;
}
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) {
return m[prop];
}
}
});
export interface MenuItemProps { export interface MenuItemProps {
onSelected?(): void; onSelected?(): void;
disabled?: boolean;
children?: ReactNode;
} }
export const MenuItem: FC<MenuItemProps> = findModuleChild((m) => { export const MenuItem: FC<MenuItemProps> = findModuleChild((m) => {

View File

@@ -1,9 +1,33 @@
import { FC, ReactNode } from 'react'; import { FC, ReactNode } from 'react';
import { findModuleChild } from '../webpack'; import { findModuleChild } from '../webpack';
// TODO: there is another argument, figure out what it does // All of the popout options + strTitle are related. Proper usage is not yet known...
export const showModal: (children: ReactNode, parent?: EventTarget) => void = findModuleChild((m) => { export interface ShowModalProps {
browserContext?: unknown; // This is another Deck Object that is yet to be found
bForcePopOut?: boolean;
bHideActionIcons?: boolean;
bHideMainWindowForPopouts?: boolean;
bNeverPopOut?: boolean;
fnOnClose?: () => void; // Seems to be the same as "closeModal" callback, but only when the modal is a popout. Will no longer work after "Update" invocation!
popupHeight?: number;
popupWidth?: number;
promiseRenderComplete?: Promise<void>; // Invoked once the render is complete. Currently, it seems to be used as image loading success/error callback...
strTitle?: string;
}
export interface ShowModalResult {
// This method will not invoke any of the variations of "closeModal" callbacks!
Close: () => void;
// This method will replace the modal element completely and will not update the callback chains,
// meaning that "closeModal" and etc. will not automatically close the modal anymore (also "fnOnClose"
// will not be even called upon close anymore)! You have to manually call the "Close" method when, for example,
// the "closeModal" is invoked in the newly updated modal:
// <ModalRoot closeModal={() => { console.log("ABOUT TO CLOSE"); showModalRes.Close(); }} />
Update: (modal: ReactNode) => void;
}
export const showModal: (modal: ReactNode, parent?: EventTarget, props?: ShowModalProps) => Promise<ShowModalResult> = findModuleChild((m) => {
if (typeof m !== 'object') return undefined; if (typeof m !== 'object') return undefined;
for (let prop in m) { for (let prop in m) {
if (typeof m[prop] === 'function' && m[prop].toString().includes('bHideMainWindowForPopouts:!0')) { if (typeof m[prop] === 'function' && m[prop].toString().includes('bHideMainWindowForPopouts:!0')) {
@@ -13,6 +37,7 @@ export const showModal: (children: ReactNode, parent?: EventTarget) => void = fi
}); });
export interface ModalRootProps { export interface ModalRootProps {
children?: ReactNode;
onCancel?(): void; onCancel?(): void;
closeModal?(): void; closeModal?(): void;
onOK?(): void; onOK?(): void;
@@ -24,10 +49,18 @@ export interface ModalRootProps {
bDisableBackgroundDismiss?: boolean; bDisableBackgroundDismiss?: boolean;
bHideCloseIcon?: boolean; bHideCloseIcon?: boolean;
bOKDisabled?: boolean; bOKDisabled?: boolean;
bCancelDisabled?: boolean;
} }
export interface ConfirmModalProps extends ModalRootProps { export interface ConfirmModalProps extends ModalRootProps {
onMiddleButton?(): void; onMiddleButton?(): void; // setting this prop will enable the middle button
strTitle?: ReactNode;
strDescription?: ReactNode;
strOKButtonText?: ReactNode;
strCancelButtonText?: ReactNode;
strMiddleButtonText?: ReactNode;
bAlertDialog?: boolean; // This will open a modal with only OK button enabled
bMiddleDisabled?: boolean;
} }
export const ConfirmModal = findModuleChild((m) => { export const ConfirmModal = findModuleChild((m) => {
@@ -46,4 +79,4 @@ export const ModalRoot = findModuleChild((m) => {
return m[prop]; return m[prop];
} }
} }
}) as FC<ModalRootProps>; }) as FC<ModalRootProps>;

View File

@@ -4,8 +4,14 @@ import { Module, findModuleChild } from '../webpack';
export interface SidebarNavigationPages { export interface SidebarNavigationPages {
title: string; title: string;
route: string;
content: ReactNode; content: ReactNode;
icon?: ReactNode;
visible?: boolean;
hideTitle?: boolean;
identifier?: string;
route?: string;
link?: string;
padding?: "none" | "compact";
} }
export interface SidebarNavigationProps { export interface SidebarNavigationProps {
@@ -13,6 +19,8 @@ export interface SidebarNavigationProps {
pages: SidebarNavigationPages[]; pages: SidebarNavigationPages[];
showTitle?: boolean; showTitle?: boolean;
disableRouteReporting?: boolean; disableRouteReporting?: boolean;
page?: string;
onPageRequested?: (page: string) => void;
} }
export const SidebarNavigation = findModuleChild((mod: Module) => { export const SidebarNavigation = findModuleChild((mod: Module) => {

View File

@@ -0,0 +1,47 @@
import { FC, ReactNode } from 'react';
import { findModule } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
/**
* Individual tab objects for the Tabs component
*
* @property id ID of this tab, can be used with activeTab to auto-focus a given tab
* @property title Title shown in the header bar
* @property renderTabAddon Return a {@link ReactNode} to render it next to the tab title, i.e. the counts for each tab on the Media page
* @property content Content of the tab
* @property footer Sets up button handlers and labels
*/
export interface Tab {
id: string;
title: string;
renderTabAddon?: () => ReactNode;
content: ReactNode;
footer?: FooterLegendProps;
}
/**
* Props for the {@link Tabs}
*
* @property tabs array of {@link Tab}
* @property activeTab tab to automatically focus, {@link Tab.id}
* @property onShowTab Currently unknown.
* @property autoFocusContents Whether to automatically focus the tab contents or not.
* @property footer Sets up button handlers and labels
*/
export interface TabsProps {
tabs: Tab[];
activeTab?: string;
onShowTab?: (...args: unknown[]) => void;
autoFocusContents?: boolean;
}
/**
* Tabs component as used in the library and media tabs. See {@link TabsProps}
*/
export const Tabs = Object.values(findModule((m) => {
if (typeof m !== 'object') return false;
for (let prop in m) {
if (m[prop]?.Unbleed) return true;
}
return false;
})).find((x: any) => x?.type?.toString()?.includes("((function(){")) as FC<TabsProps>;

View File

@@ -1,6 +1,7 @@
export * from './Button'; export * from './Button';
export * from './ButtonItem'; export * from './ButtonItem';
export * from './Carousel'; export * from './Carousel';
export * from './Dialog';
export * from './Dropdown'; export * from './Dropdown';
export * from './Field'; export * from './Field';
export * from './Focusable'; export * from './Focusable';

View File

@@ -1,6 +1,6 @@
import { findModule } from '../webpack'; import { findModule } from '../webpack';
type StaticClasses = Record< type QuickAccessMenuClasses = Record<
| 'ActiveTab' | 'ActiveTab'
| 'AllTabContents' | 'AllTabContents'
| 'BatteryDetailsLabels' | 'BatteryDetailsLabels'
@@ -63,7 +63,7 @@ type StaticClasses = Record<
string string
>; >;
type ScrollClasses = Record< type ScrollPanelClasses = Record<
| 'ScrollBoth' | 'ScrollBoth'
| 'ScrollPanel' | 'ScrollPanel'
| 'ScrollX' | 'ScrollX'
@@ -307,9 +307,66 @@ type PlaySectionClasses = Record<
string string
>; >;
export const staticClasses: StaticClasses = findModule((mod) => typeof mod === 'object' && mod.TransitionMenuDelay); type GamepadSliderClasses = Record<
export const scrollClasses: ScrollClasses = findModule((mod) => typeof mod === 'object' && mod.ScrollPanel && mod.ScrollY); | "error-shake-duration"
export const gamepadDialogClasses: GamepadDialogClasses = findModule((mod) => typeof mod === 'object' && mod.WithFirstRow); | "SliderControlPanelGroup"
export const quickAccessControlsClasses: QuickAccessControlsClasses = findModule((mod) => typeof mod === 'object' && mod.PanelSectionRow); | "SliderControlAndNotches"
export const updaterFieldClasses: UpdaterFieldClasses = findModule((mod) => typeof mod === 'object' && mod.PatchNotes && mod.PostedTime); | "WithDefaultValue"
export const playSectionClasses: PlaySectionClasses = findModule((mod) => typeof mod === 'object' && mod.MenuButton && mod.MenuActive); | "SliderControl"
| "Disabled"
| "SliderTrack"
| "SliderHasNotches"
| "SliderTrackDark"
| "SliderHandleContainer"
| "VerticalLineSliderHandleContainer"
| "ParenSliderHandleContainer"
| "SliderHandle"
| "SliderHandleFocusPop"
| "VerticalLineSliderHandle"
| "ParenSliderHandle"
| "Left"
| "SliderControlWithIcon"
| "Icon"
| "SliderNotchContainer"
| "SliderNotch"
| "AlignToEnds"
| "SliderNotchLabel"
| "AlignToLeft"
| "AlignToRight"
| "SliderNotchTick"
| "TickActive"
| "LabelText"
| "DescriptionValue"
| "EditableValue"
| "FakeEditableValue"
| "RedBorder"
| "EditableValueSuffix"
| "ErrorShake"
| "error-shake"
| "CompoundSlider"
| "CompoundSliderSubSlider"
| "Right"
| "CompoundSliderSubSliderLabelContainer"
| "CompoundSliderSubSliderLabelPositioner"
| "CompoundSliderSubSliderLabel"
| "CompoundSliderSubSliderLabelInternal"
| "DefaultValueTickContainer"
| "DefaultValueTick",
string
>;
export const quickAccessMenuClasses: QuickAccessMenuClasses = findModule((mod) => typeof mod === 'object' && mod?.Title?.includes('quickaccessmenu'));
/**
* @depreciated please use quickAccessMenuClasses instead
*/
export const staticClasses = quickAccessMenuClasses;
export const scrollPanelClasses: ScrollPanelClasses = findModule((mod) => typeof mod === 'object' && mod?.ScrollPanel?.includes('scrollpanel'));
/**
* @depreciated please use scrollPanelClasses instead
*/
export const scrollClasses = scrollPanelClasses;
export const gamepadDialogClasses: GamepadDialogClasses = findModule((mod) => typeof mod === 'object' && mod?.GamepadDialogContent?.includes('gamepaddialog'));
export const quickAccessControlsClasses: QuickAccessControlsClasses = findModule((mod) => typeof mod === 'object' && mod?.PanelSection?.includes('quickaccesscontrols'));
export const updaterFieldClasses: UpdaterFieldClasses = findModule((mod) => typeof mod === 'object' && mod?.OOBEUpdateStatusContainer?.includes('updaterfield'));
export const playSectionClasses: PlaySectionClasses = findModule((mod) => typeof mod === 'object' && mod?.Container?.includes('appdetailsplaysection'));
export const gamepadSliderClasses: GamepadSliderClasses = findModule((mod) => typeof mod === 'object' && mod?.SliderControlPanelGroup?.includes('gamepadslider'));

1
src/deck-hooks/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './useParams'

View File

@@ -0,0 +1,15 @@
import { ReactRouter } from "../webpack";
/**
* Get the current params from ReactRouter
*
* @returns an object with the current ReactRouter params
*
* @example
* import { useParams } from "decky-frontend-lib";
*
* const { appid } = useParams<{ appid: string }>()
*/
export const useParams = Object.values(ReactRouter).find((val) =>
/return (\w)\?\1\.params:{}/.test(`${val}`)
) as <T>() => T

View File

@@ -1,6 +1,8 @@
// export * from './deck-libs'; // export * from './deck-libs';
export * from './custom-components'; export * from './custom-components';
export * from './custom-hooks';
export * from './deck-components'; export * from './deck-components';
export * from './deck-hooks'
export * from './plugin'; export * from './plugin';
export * from './webpack'; export * from './webpack';
export * from './utils'; export * from './utils';

5
typedoc.json Normal file
View File

@@ -0,0 +1,5 @@
{
"githubPages": false,
"categorizeByGroup": false,
"excludeExternals": true
}