Compare commits

...

17 Commits

Author SHA1 Message Date
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
11 changed files with 369 additions and 21 deletions

View File

@@ -1,3 +1,38 @@
# [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) ## [3.1.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.1.2...v3.1.3) (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.

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "decky-frontend-lib", "name": "decky-frontend-lib",
"version": "3.1.3", "version": "3.3.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "decky-frontend-lib", "name": "decky-frontend-lib",
"version": "3.1.3", "version": "3.3.0",
"license": "GPL-2.0-or-later", "license": "GPL-2.0-or-later",
"dependencies": { "dependencies": {
"minimist": "^1.2.6" "minimist": "^1.2.6"

View File

@@ -1,6 +1,6 @@
{ {
"name": "decky-frontend-lib", "name": "decky-frontend-lib",
"version": "3.1.3", "version": "3.3.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",
@@ -30,7 +30,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"
}, },

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

@@ -1,8 +1,9 @@
import { CSSProperties, FC, RefAttributes } from 'react'; import { CSSProperties, FC, RefAttributes } from 'react';
import { CommonUIModule } from '../webpack'; import { CommonUIModule } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
export interface DialogButtonProps extends RefAttributes<HTMLDivElement> { export interface DialogButtonProps extends RefAttributes<HTMLDivElement>, FooterLegendProps {
label?: string; label?: string;
style?: CSSProperties; style?: CSSProperties;
className?: string; className?: string;

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')) {
@@ -24,10 +48,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 +78,4 @@ export const ModalRoot = findModuleChild((m) => {
return m[prop]; return m[prop];
} }
} }
}) as FC<ModalRootProps>; }) as FC<ModalRootProps>;

View File

@@ -307,9 +307,58 @@ type PlaySectionClasses = Record<
string string
>; >;
type GamepadSliderClasses = Record<
| "error-shake-duration"
| "SliderControlPanelGroup"
| "SliderControlAndNotches"
| "WithDefaultValue"
| "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 staticClasses: StaticClasses = findModule((mod) => typeof mod === 'object' && mod.TransitionMenuDelay); export const staticClasses: StaticClasses = findModule((mod) => typeof mod === 'object' && mod.TransitionMenuDelay);
export const scrollClasses: ScrollClasses = findModule((mod) => typeof mod === 'object' && mod.ScrollPanel && mod.ScrollY); export const scrollClasses: ScrollClasses = findModule((mod) => typeof mod === 'object' && mod.ScrollPanel && mod.ScrollY);
export const gamepadDialogClasses: GamepadDialogClasses = findModule((mod) => typeof mod === 'object' && mod.WithFirstRow); export const gamepadDialogClasses: GamepadDialogClasses = findModule((mod) => typeof mod === 'object' && mod.WithFirstRow);
export const quickAccessControlsClasses: QuickAccessControlsClasses = findModule((mod) => typeof mod === 'object' && mod.PanelSectionRow); export const quickAccessControlsClasses: QuickAccessControlsClasses = findModule((mod) => typeof mod === 'object' && mod.PanelSectionRow);
export const updaterFieldClasses: UpdaterFieldClasses = findModule((mod) => typeof mod === 'object' && mod.PatchNotes && mod.PostedTime); export const updaterFieldClasses: UpdaterFieldClasses = findModule((mod) => typeof mod === 'object' && mod.PatchNotes && mod.PostedTime);
export const playSectionClasses: PlaySectionClasses = findModule((mod) => typeof mod === 'object' && mod.MenuButton && mod.MenuActive); export const playSectionClasses: PlaySectionClasses = findModule((mod) => typeof mod === 'object' && mod.MenuButton && mod.MenuActive);
export const gamepadSliderClasses: GamepadSliderClasses = findModule((mod) => typeof mod === 'object' && mod.SliderTrack && mod.SliderHasNotches);