Skip to content

Commit a398358

Browse files
committed
move global lighting settings to new button
1 parent fe6a3a1 commit a398358

2 files changed

Lines changed: 90 additions & 28 deletions

File tree

Keypad.Flasher.Client/src/KeypadFlasherApp.tsx

Lines changed: 84 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -214,26 +214,10 @@ export default function KeypadFlasherApp() {
214214
muted={false}
215215
/>
216216
<div className="muted small" style={{ maxWidth: "340px" }}>
217-
Hold to see active lighting, release to return to passive. Does not show global brightness. Updates live as you change settings below.
217+
Hold to see active lighting, release to return to passive. Does not show global brightness. Updates live when settings change.
218218
</div>
219219
</div>
220220
</div>
221-
<div style={{ display: "flex", flexDirection: "column", gap: "4px", marginTop: "8px" }}>
222-
<div style={{ fontWeight: 600 }}>Device lighting</div>
223-
<div className="muted small">These settings apply to every LED on the device.</div>
224-
<label className="muted small" style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: "10px" }}>
225-
<span>Global brightness</span>
226-
<input
227-
type="range"
228-
min={0}
229-
max={100}
230-
value={draftLedConfig.brightnessPercent}
231-
onChange={(e) => setBrightnessPercent(Number(e.target.value))}
232-
/>
233-
<span style={{ minWidth: "36px", textAlign: "right" }}>{draftLedConfig.brightnessPercent}%</span>
234-
</label>
235-
<div className="muted small">Brightness scales both passive and active effects together; use the controls below for per-key tweaks.</div>
236-
</div>
237221
<div style={{ display: "flex", flexDirection: "column", gap: "14px" }}>
238222
<div style={{ display: "flex", flexDirection: "column", gap: "2px" }}>
239223
<div style={{ fontWeight: 600 }}>Passive lighting</div>
@@ -362,6 +346,7 @@ export default function KeypadFlasherApp() {
362346
const [editorTarget, setEditorTarget] = useState<EditTarget | null>(null);
363347
const [editorBinding, setEditorBinding] = useState<HidBindingDto | null>(null);
364348
const [stepClipboard, setStepClipboard] = useState<HidStepDto[] | null>(null);
349+
const [showGlobalLightingModal, setShowGlobalLightingModal] = useState<boolean>(false);
365350
const [showLightingModal, setShowLightingModal] = useState<boolean>(false);
366351
const [focusLedIndex, setFocusLedIndex] = useState<number | null>(null);
367352
const [copiedLedLighting, setCopiedLedLighting] = useState<{ passiveMode: PassiveLedMode; passive: LedColor; activeMode: ActiveLedMode; activeColor: LedColor } | null>(null);
@@ -913,6 +898,17 @@ export default function KeypadFlasherApp() {
913898
return { r, g, b };
914899
};
915900

901+
const cloneLedConfig = (config: LedConfigurationDto): LedConfigurationDto => ({
902+
passiveModes: [...config.passiveModes],
903+
passiveColors: [...config.passiveColors],
904+
activeModes: [...config.activeModes],
905+
activeColors: [...config.activeColors],
906+
brightnessPercent: config.brightnessPercent,
907+
rainbowStepMs: config.rainbowStepMs,
908+
breathingMinPercent: config.breathingMinPercent,
909+
breathingStepMs: config.breathingStepMs,
910+
});
911+
916912
const ledDisplayName = (idx: number): string => {
917913
const btn = userButtons.find((b) => b.ledIndex === idx);
918914
if (btn) return `Button ${btn.id + 1}`;
@@ -1021,6 +1017,26 @@ export default function KeypadFlasherApp() {
10211017
}
10221018
}, [parseImportedConfig, connectedInfo, rememberedBootloaderId, selectedLayout]);
10231019

1020+
const closeGlobalLightingModal = () => {
1021+
setShowGlobalLightingModal(false);
1022+
setDraftLedConfig(null);
1023+
};
1024+
1025+
const saveGlobalLightingModal = () => {
1026+
if (draftLedConfig) {
1027+
setLedConfig(draftLedConfig);
1028+
}
1029+
closeGlobalLightingModal();
1030+
};
1031+
1032+
const openGlobalLightingModal = () => {
1033+
if (!ledConfig) return;
1034+
setDraftLedConfig(cloneLedConfig(ledConfig));
1035+
setLightingStatus(defaultLightingStatus);
1036+
setFocusLedIndex(null);
1037+
setShowGlobalLightingModal(true);
1038+
};
1039+
10241040
const closeLightingModal = () => {
10251041
setShowLightingModal(false);
10261042
setDraftLedConfig(null);
@@ -1035,16 +1051,7 @@ export default function KeypadFlasherApp() {
10351051
};
10361052
const openLightingForLed = (idx: number) => {
10371053
if (!ledConfig || idx < 0 || idx >= ledConfig.passiveColors.length) return;
1038-
setDraftLedConfig({
1039-
passiveModes: [...ledConfig.passiveModes],
1040-
passiveColors: [...ledConfig.passiveColors],
1041-
activeModes: [...ledConfig.activeModes],
1042-
activeColors: [...ledConfig.activeColors],
1043-
brightnessPercent: ledConfig.brightnessPercent,
1044-
rainbowStepMs: ledConfig.rainbowStepMs,
1045-
breathingMinPercent: ledConfig.breathingMinPercent,
1046-
breathingStepMs: ledConfig.breathingStepMs,
1047-
});
1054+
setDraftLedConfig(cloneLedConfig(ledConfig));
10481055
setLightingStatus(defaultLightingStatus);
10491056
setFocusLedIndex(idx);
10501057
setShowLightingModal(true);
@@ -1387,6 +1394,8 @@ export default function KeypadFlasherApp() {
13871394
warnSingleChord={warnSingleChord}
13881395
onEdit={openEdit}
13891396
onOpenLightingForLed={openLightingForLed}
1397+
onOpenLightingSettings={openGlobalLightingModal}
1398+
lightingDisabled={!ledConfig || layoutLedCount === 0}
13901399
onToggleBootloaderOnBoot={updateBootloaderOnBoot}
13911400
onToggleBootloaderChord={updateBootloaderChordMember}
13921401
onExportConfig={openExportModal}
@@ -1458,6 +1467,54 @@ export default function KeypadFlasherApp() {
14581467
</div>
14591468
)}
14601469

1470+
{showGlobalLightingModal && (
1471+
<div
1472+
className="modal-backdrop"
1473+
role="dialog"
1474+
aria-modal="true"
1475+
onClick={(e) => {
1476+
if (modalPointerDownRef.current) { modalPointerDownRef.current = false; return; }
1477+
if (e.target === e.currentTarget) closeGlobalLightingModal();
1478+
}}
1479+
>
1480+
<div
1481+
className="modal lighting-modal"
1482+
onClick={(e) => e.stopPropagation()}
1483+
onMouseDown={() => { modalPointerDownRef.current = true; }}
1484+
onMouseUp={() => { modalPointerDownRef.current = false; }}
1485+
>
1486+
<div className="modal-header">
1487+
<div className="modal-title">Global lighting</div>
1488+
</div>
1489+
<div className="modal-body">
1490+
{draftLedConfig ? (
1491+
<div className="space-y-2">
1492+
<div className="muted small">These settings apply to every button LED on the device.</div>
1493+
<label className="muted small" style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: "10px" }}>
1494+
<span>Global brightness</span>
1495+
<input
1496+
type="range"
1497+
min={0}
1498+
max={100}
1499+
value={draftLedConfig.brightnessPercent}
1500+
onChange={(e) => setBrightnessPercent(Number(e.target.value))}
1501+
/>
1502+
<span style={{ minWidth: "36px", textAlign: "right" }}>{draftLedConfig.brightnessPercent}%</span>
1503+
</label>
1504+
<div className="muted small">Brightness scales both passive and active effects together.</div>
1505+
</div>
1506+
) : (
1507+
<div className="muted small">No lighting configuration available.</div>
1508+
)}
1509+
</div>
1510+
<div className="modal-actions">
1511+
<button className="btn" onClick={closeGlobalLightingModal}>Cancel</button>
1512+
<button className="btn btn-primary" onClick={saveGlobalLightingModal} disabled={!draftLedConfig}>Save</button>
1513+
</div>
1514+
</div>
1515+
</div>
1516+
)}
1517+
14611518
{showLightingModal && selectedLayout && (
14621519
<div
14631520
className="modal-backdrop"

Keypad.Flasher.Client/src/components/LayoutPreview.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ type LayoutPreviewProps = {
1515
warnSingleChord: boolean;
1616
onEdit: (target: EditTarget) => void;
1717
onOpenLightingForLed: (ledIndex: number) => void;
18+
onOpenLightingSettings?: () => void;
19+
lightingDisabled?: boolean;
1820
onToggleBootloaderOnBoot: (target: EditTarget, value: boolean) => void;
1921
onToggleBootloaderChord: (target: EditTarget, value: boolean) => void;
2022
onResetDefaults?: () => void;
@@ -23,7 +25,7 @@ type LayoutPreviewProps = {
2325
canReset?: boolean;
2426
};
2527

26-
export function LayoutPreview({ layout, layoutRows, buttonBindings, encoderBindings, ledConfig, warnNoBootEntry, warnSingleChord, onEdit, onOpenLightingForLed, onToggleBootloaderOnBoot, onToggleBootloaderChord, onResetDefaults, onExportConfig, onImportConfig, canReset }: LayoutPreviewProps) {
28+
export function LayoutPreview({ layout, layoutRows, buttonBindings, encoderBindings, ledConfig, warnNoBootEntry, warnSingleChord, onEdit, onOpenLightingForLed, onOpenLightingSettings, lightingDisabled, onToggleBootloaderOnBoot, onToggleBootloaderChord, onResetDefaults, onExportConfig, onImportConfig, canReset }: LayoutPreviewProps) {
2729
const sortedButtons = [...layout.buttons].sort((a, b) => a.id - b.id);
2830
const encoderCount = layout.encoders.length;
2931
const [confirmReset, setConfirmReset] = useState(false);
@@ -148,6 +150,9 @@ export function LayoutPreview({ layout, layoutRows, buttonBindings, encoderBindi
148150
<div className="muted small">Click any button or encoder tile to change its bindings or lighting if available.</div>
149151
</div>
150152
<div className="layout-actions">
153+
{onOpenLightingSettings && (
154+
<button className="btn btn-primary" onClick={onOpenLightingSettings} disabled={lightingDisabled} title={lightingDisabled ? "No LEDs available" : undefined}>Global Lighting</button>
155+
)}
151156
{onExportConfig && <button className="btn" onClick={onExportConfig}>Export</button>}
152157
{onImportConfig && <button className="btn" onClick={onImportConfig}>Import</button>}
153158
{canReset && onResetDefaults && (

0 commit comments

Comments
 (0)