Should be done

This commit is contained in:
HannahPadd
2026-04-01 07:55:15 +02:00
parent cbf59e0ec7
commit f938d7e49c
7 changed files with 167 additions and 144 deletions

View File

@@ -1,5 +1,6 @@
import { useState, forwardRef, useRef } from 'react';
import { Typography } from './Typography';
import classNames from 'classnames';
const excludedKeys = [' ', 'SPACE', 'META'];
const maxKeybindLength = 4;
@@ -68,19 +69,12 @@ export const KeybindRecorder = forwardRef<
};
return (
<div className="relative w-full justify-center align-center">
{showError ? (
<div className="absolute bottom isInvalid keyslot-invalid text-red-600">
<Typography color="red-600">{errorText}</Typography>
</div>
) : (
''
)}
<div className="flex gap-2 m-2 items-center rounded-lg">
<div className="w-full justify-center items-center flex flex-col gap-2">
<div className="flex gap-2 p-2 items-center rounded-lg relative">
<input
autoFocus
ref={inputRef}
className="opacity-0 absolute inset-0 cursor-pointer"
className="opacity-0 absolute cursor-pointer w-full"
onFocus={handleOnFocus}
onBlur={handleOnBlur}
onKeyDown={handleKeyDown}
@@ -93,20 +87,15 @@ export const KeybindRecorder = forwardRef<
return (
<div key={i} className="flex flex-row">
<div
className={`
flex p-2 rounded-lg min-w-[50px] min-h-[50px] text-lg justify-center items-center bg-background-80 mobile:text-sm
${
isInvalid
? 'keyslot-invalid ring-2 ring-red-600'
: isActive
? 'keyslot-animate ring-2 ring-accent'
: 'ring-accent'
}
`}
className={classNames('flex p-2 rounded-lg min-w-[50px] min-h-[50px] text-main-title justify-center items-center bg-background-80 mobile:text-sm', {
'keyslot-invalid ring-2 ring-status-critical': isInvalid,
'keyslot-animate ring-2 ring-accent': isActive && !isInvalid,
'ring-accent': !isInvalid && !isInvalid
})}
>
{key ?? ''}
</div>
<div className="flex pl-2 text-lg justify-center items-center mobile:text-sm">
<div className="flex pl-2 text-main-title justify-center items-center mobile:text-sm">
{i < maxKeybindLength - 1 ? '+' : ''}
</div>
</div>
@@ -114,6 +103,11 @@ export const KeybindRecorder = forwardRef<
})}
</div>
</div>
{showError && (
<div className="isInvalid keyslot-invalid">
<Typography color="text-status-critical">{errorText}</Typography>
</div>
)}
</div>
);
});

View File

@@ -29,49 +29,50 @@ export function KeybindRecorderModal({
return (
<BaseModal
isOpen={isVisisble}
appendClasses="w-full max-w-xl h-full max-h-52"
onRequestClose={() => onClose()}
appendClasses="w-full max-w-xl"
>
<div className="flex-col gap-4 w-full">
<div className="flex flex-col w-full">
<div className="flex flex-col gap-3 w-full">
<Typography variant="section-title">
{l10n.getString('settings-keybinds-recorder-modal-title')}{' '}
{l10n.getString(keybindlocalization)}
</Typography>
<Controller
control={control}
name={name}
render={({ field }) => (
<KeybindRecorder
keys={field.value ?? []}
onKeysChange={field.onChange}
ref={field.ref}
/>
)}
<div className="flex flex-col gap-3 w-full justify-between h-full">
<Typography variant="section-title">
{l10n.getString('settings-keybinds-recorder-modal-title')}{' '}
{l10n.getString(keybindlocalization)}
</Typography>
<Controller
control={control}
name={name}
render={({ field }) => (
<KeybindRecorder
keys={field.value ?? []}
onKeysChange={field.onChange}
ref={field.ref}
/>
)}
/>
<div className="flex flex-row justify-between w-full">
<div className="flex flex-row justify-start gap-4">
<Button
id="settings-keybinds-recorder-modal-reset-button"
variant="tertiary"
onClick={() => {
resetField(name);
onClose()
}}
/>
<Button
id="settings-keybinds-recorder-modal-unbind-button"
variant="tertiary"
onClick={() => {
onUnbind()
onClose()
}}
/>
</div>
<div className="flex flex-row justify-end">
<Button
id="settings-keybinds-recorder-modal-done-button"
variant="primary"
onClick={onClose}
/>
<div className="flex flex-row justify-between w-full">
<div className="flex flex-row justify-start gap-4">
<Button
id="settings-keybinds-recorder-modal-reset-button"
variant="primary"
onClick={() => {
resetField(name);
}}
/>
<Button
id="settings-keybinds-recorder-modal-unbind-button"
variant="primary"
onClick={onUnbind}
/>
</div>
<div className="flex flex-row justify-end">
<Button
id="settings-keybinds-recorder-modal-done-button"
variant="primary"
onClick={onClose}
/>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,14 +1,14 @@
import { Typography } from './Typography';
import './KeybindRow.scss';
import { ReactNode, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { Control, UseFormGetValues } from 'react-hook-form';
import { NumberSelector } from './NumberSelector';
import { useLocaleConfig } from '@/i18n/config';
const createKeybindDisplay = (keybind: string[]): ReactNode | null => {
function KeyBindKeyList({ keybind }: { keybind: string[] }) {
if (keybind.length <= 1) {
return (
<div className="flex min-h-[50px] h-full text-lg items-center justifiy-center">
<div className="flex h-full text-section-title items-center justifiy-center">
Click to edit keybind
</div>
);
@@ -16,10 +16,10 @@ const createKeybindDisplay = (keybind: string[]): ReactNode | null => {
return keybind.map((key, i) => {
return (
<div key={i} className="flex flex-row">
<div className="flex flex-wrap p-2 rounded-lg min-w-[50px] min-h-[50px] text-lg justify-center items-center bg-background-80 mobile:text-sm">
<div className="flex flex-wrap p-2 rounded-lg min-w-[50px] text-standard-bold justify-center items-center bg-background-80 mobile:text-sm">
{key ?? ''}
</div>
<div className="flex justify-center items-center text-lg mobile:text-sm gap-2 pl-3">
<div className="flex justify-center items-center text-section-title mobile:text-sm gap-2 pl-3">
{i < keybind.length - 1 ? '+' : ''}
</div>
</div>
@@ -40,7 +40,6 @@ export function KeybindsRow({
getValue: UseFormGetValues<any>;
openKeybindRecorderModal: (index: number) => void;
}) {
const [keybindDisplay, setKeybindDisplay] = useState<ReactNode>(null);
const [binding, setBinding] = useState<string[]>();
const { currentLocales } = useLocaleConfig();
const secondsFormat = new Intl.NumberFormat(currentLocales, {
@@ -58,21 +57,17 @@ export function KeybindsRow({
setBinding(getValue(`keybinds.${index}.binding`));
});
useEffect(() => {
if (binding != null) setKeybindDisplay(createKeybindDisplay(binding));
}, [binding]);
return (
<div className="keybind-row bg-background-60 rounded-xl h-full hover:ring-2 hover:ring-accent mobile:flex mobile:flex-wrap mobile:justify-center mobile:items-center">
<label className="text-sm font-medium text-background-10 p-2">
<div className="keybind-row bg-background-60 rounded-xl h-full keybinds-small:flex keybinds-small:flex-col keybinds-small:justify-center keybinds-small:items-center p-2">
<label className="text-sm font-medium text-background-10 keybinds-small:flex keybinds-small:py-2 keybinds-small:justify-center keybinds-small:align-middle">
<Typography id={`settings-keybinds_${id}`} />
</label>
<div
className="flex gap-2 min-h-[42px] items-center m-2 rounded-lg bg-background-70 hover:bg-background-50"
className="flex gap-2 h-full items-center rounded-lg bg-background-70 hover:bg-background-50 w-full"
onClick={handleOpenModal}
>
<div className="flex flex-grow gap-2 m-2 justify-center h-full">
{keybindDisplay}
<div className="flex flex-grow gap-2 justify-center p-2 h-[50px]">
{binding != null && <KeyBindKeyList keybind={binding} />}
</div>
</div>
<NumberSelector

View File

@@ -6,11 +6,11 @@
'n v d ' auto
'k k k ' auto
'b . . ' auto
/ 1fr 1fr 80px;
/ 1fr 1fr 90px;
grid-template-columns: subgrid;
grid-template-rows: subgrid;
align-items: left;
gap: 20px;
gap: 10px;
}

View File

@@ -16,9 +16,12 @@ import {
KeybindRequestT,
KeybindResponseT,
KeybindT,
OpenUriRequestT,
RpcMessage,
} from 'solarxr-protocol';
import { useFieldArray, useForm } from 'react-hook-form';
import { useAppContext } from '@/hooks/app';
import { useElectron } from '@/hooks/electron';
export type KeybindForm = {
keybinds: {
@@ -30,6 +33,7 @@ export type KeybindForm = {
};
export function KeybindSettings() {
const electron = useElectron();
const { l10n } = useLocalization();
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
const [isOpen, setIsOpen] = useState<boolean>(false);
@@ -39,6 +43,8 @@ export function KeybindSettings() {
}
);
const currentIndex = useRef<number | null>(null);
const { installInfo } = useAppContext();
const { control, resetField, handleSubmit, reset, setValue, getValues } =
useForm<KeybindForm>({
@@ -66,6 +72,10 @@ export function KeybindSettings() {
});
};
const handleOpenSystemSettingsButton = () => {
sendRPCPacket(RpcMessage.OpenUriRequest, new OpenUriRequestT());
};
useRPCPacket(
RpcMessage.KeybindResponse,
({ keybind, defaultKeybinds }: KeybindResponseT) => {
@@ -127,62 +137,82 @@ export function KeybindSettings() {
return (
<SettingsPageLayout>
<SettingsPagePaneLayout icon={<WrenchIcon />} id="keybinds">
<Typography variant="main-title" id="settings-keybinds" />
<div className="flex flex-col pt-2 pb-4">
{l10n
.getString('settings-keybinds-description')
.split('\n')
.map((line, i) => (
<Typography key={i}>{line}</Typography>
))}
<div className="flex flex-col gap-2">
<Typography variant="main-title" id="settings-keybinds" />
<div className="flex flex-col pt-2 pb-4">
{l10n
.getString('settings-keybinds-description')
.split('\n')
.map((line, i) => (
<Typography key={i}>{line}</Typography>
))}
</div>
{installInfo?.isWayland ? (
<div className="flex flex-col gap-4">
<Typography id="settings-keybinds-wayland-description" />
<div>
<Button
id="settings-keybinds-wayland-open-system-settings-button"
className="flex flex-col"
onClick={handleOpenSystemSettingsButton}
variant="primary"
/>
</div>
</div>
) : electron.isElectron && electron.data().os.type === 'windows' && (
<>
<div className="keybind-settings">
<Typography
id="keybind_config-keybind_name"
variant="section-title"
/>
<Typography
id="keybind_config-keybind_value"
variant="section-title"
/>
<Typography
id="keybind_config-keybind_delay"
variant="section-title"
/>
{createKeybindRows()}
</div>
<div className="flex justify-end">
<Button
id="settings-keybinds_reset-all-button"
onClick={() => {
reset(defaultKeybindsState);
handleSubmit(onSubmit)();
}}
variant="primary"
/>
</div>
<KeybindRecorderModal
id={
currentIndex.current != null
? fields[currentIndex.current].name
: ''
}
control={control}
resetField={resetField}
name={
currentIndex.current != null
? `keybinds.${currentIndex.current}.binding`
: ''
}
isVisisble={isOpen}
onClose={() => {
setIsOpen(false);
handleSubmit(onSubmit)();
}}
onUnbind={() => {
if (currentIndex.current != null)
setValue(`keybinds.${currentIndex.current}.binding`, []);
}}
/>
</>
)}
</div>
<div className="keybind-settings">
<Typography
id="keybind_config-keybind_name"
variant="section-title"
/>
<Typography
id="keybind_config-keybind_value"
variant="section-title"
/>
<Typography
id="keybind_config-keybind_delay"
variant="section-title"
/>
{createKeybindRows()}
<Button
id="settings-keybinds_reset-all-button"
className="justify-self-start"
onClick={() => {
reset(defaultKeybindsState);
handleSubmit(onSubmit)();
}}
variant="primary"
/>
</div>
<KeybindRecorderModal
id={
currentIndex.current != null
? fields[currentIndex.current].name
: ''
}
control={control}
resetField={resetField}
name={
currentIndex.current != null
? `keybinds.${currentIndex.current}.binding`
: ''
}
isVisisble={isOpen}
onClose={() => {
setIsOpen(false);
handleSubmit(onSubmit)();
}}
onUnbind={() => {
if (currentIndex.current != null)
setValue(`keybinds.${currentIndex.current}.binding`, []);
}}
/>
</SettingsPagePaneLayout>
</SettingsPageLayout>
);

View File

@@ -59,16 +59,6 @@ export function useProvideAppContext(): AppContext {
}
});
useEffect(() => {
sendRPCPacket(RpcMessage.InstalledInfoRequest, new InstalledInfoResponseT());
}, []);
useRPCPacket(
RpcMessage.InstalledInfoResponse,
({ isUdevInstalled, isWayland }: InstalledInfoResponseT) => {
setInstallInfo(new InstalledInfoResponseT(isUdevInstalled, isWayland));
}
);
useEffect(() => {
updateSentryContext(devices);
@@ -90,6 +80,18 @@ export function useProvideAppContext(): AppContext {
};
}, [config?.uuid]);
useEffect(() => {
sendRPCPacket(RpcMessage.InstalledInfoRequest, new InstalledInfoResponseT());
}, []);
useRPCPacket(
RpcMessage.InstalledInfoResponse,
({ isUdevInstalled, isWayland }: InstalledInfoResponseT) => {
setInstallInfo(new InstalledInfoResponseT(isUdevInstalled, isWayland));
}
);
useLayoutEffect(() => {
changeLocales([config?.lang || DEFAULT_LOCALE]);
}, []);

View File

@@ -183,6 +183,7 @@ const config = {
lg: '1300px',
xl: '1600px',
tall: { raw: '(min-height: 860px)' },
'keybinds-small': { raw: 'not (min-width: 1230px)' },
},
extend: {
colors: {