mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-23 09:51:10 +01:00
Initial settings implementation
This commit is contained in:
33
src-web/components/SettingsDialog.tsx
Normal file
33
src-web/components/SettingsDialog.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useSettings } from '../hooks/useSettings';
|
||||
import { useTheme } from '../hooks/useTheme';
|
||||
import { useUpdateSettings } from '../hooks/useUpdateSettings';
|
||||
import { Checkbox } from './core/Checkbox';
|
||||
import { VStack } from './core/Stacks';
|
||||
|
||||
export const SettingsDialog = () => {
|
||||
const { appearance, toggleAppearance } = useTheme();
|
||||
const settings = useSettings();
|
||||
const updateSettings = useUpdateSettings();
|
||||
|
||||
if (settings == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<VStack space={2}>
|
||||
<Checkbox
|
||||
checked={settings.validateCertificates}
|
||||
title="Validate TLS Certificates"
|
||||
onChange={(validateCertificates) =>
|
||||
updateSettings.mutateAsync({ ...settings, validateCertificates })
|
||||
}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={settings.followRedirects}
|
||||
title="Follow Redirects"
|
||||
onChange={(followRedirects) => updateSettings.mutateAsync({ ...settings, followRedirects })}
|
||||
/>
|
||||
<Checkbox checked={appearance === 'dark'} title="Dark Mode" onChange={toggleAppearance} />
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
@@ -13,6 +13,7 @@ import { IconButton } from './core/IconButton';
|
||||
import { VStack } from './core/Stacks';
|
||||
import { useDialog } from './DialogContext';
|
||||
import { KeyboardShortcutsDialog } from './KeyboardShortcutsDialog';
|
||||
import { SettingsDialog } from './SettingsDialog';
|
||||
|
||||
export function SettingsDropdown() {
|
||||
const importData = useImportData();
|
||||
@@ -61,16 +62,11 @@ export function SettingsDropdown() {
|
||||
leftSlot: <Icon icon="upload" />,
|
||||
onSelect: () => exportData.mutate(),
|
||||
},
|
||||
{
|
||||
key: 'appearance',
|
||||
label: 'Toggle Theme',
|
||||
onSelect: toggleAppearance,
|
||||
leftSlot: <Icon icon={appearance === 'dark' ? 'sun' : 'moon'} />,
|
||||
},
|
||||
{
|
||||
key: 'hotkeys',
|
||||
label: 'Keyboard shortcuts',
|
||||
hotkeyAction: 'hotkeys.showHelp',
|
||||
leftSlot: <Icon icon="keyboard" />,
|
||||
onSelect: () => {
|
||||
dialog.show({
|
||||
id: 'hotkey-help',
|
||||
@@ -79,7 +75,20 @@ export function SettingsDropdown() {
|
||||
render: () => <KeyboardShortcutsDialog />,
|
||||
});
|
||||
},
|
||||
leftSlot: <Icon icon="keyboard" />,
|
||||
},
|
||||
{
|
||||
key: 'settings',
|
||||
label: 'Settings',
|
||||
hotkeyAction: 'settings.show',
|
||||
leftSlot: <Icon icon="gear" />,
|
||||
onSelect: () => {
|
||||
dialog.show({
|
||||
id: 'settings',
|
||||
size: 'md',
|
||||
title: 'Settings',
|
||||
render: () => <SettingsDialog />,
|
||||
});
|
||||
},
|
||||
},
|
||||
{ type: 'separator', label: `Yaak v${appVersion.data}` },
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import classNames from 'classnames';
|
||||
import { useCallback } from 'react';
|
||||
import { Icon } from './Icon';
|
||||
import { HStack } from './Stacks';
|
||||
|
||||
interface Props {
|
||||
checked: boolean;
|
||||
@@ -8,33 +8,47 @@ interface Props {
|
||||
onChange: (checked: boolean) => void;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
hideLabel?: boolean;
|
||||
}
|
||||
|
||||
export function Checkbox({ checked, onChange, className, disabled, title }: Props) {
|
||||
const handleClick = useCallback(() => {
|
||||
onChange(!checked);
|
||||
}, [onChange, checked]);
|
||||
|
||||
export function Checkbox({ checked, onChange, className, disabled, title, hideLabel }: Props) {
|
||||
return (
|
||||
<button
|
||||
role="checkbox"
|
||||
aria-checked={checked ? 'true' : 'false'}
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
title={title}
|
||||
className={classNames(
|
||||
className,
|
||||
'flex-shrink-0 w-4 h-4 border border-gray-200 rounded',
|
||||
'focus:border-focus',
|
||||
'disabled:opacity-disabled',
|
||||
checked && 'bg-gray-200/10',
|
||||
// Remove focus style
|
||||
'outline-none',
|
||||
)}
|
||||
<HStack
|
||||
as="label"
|
||||
space={2}
|
||||
alignItems="center"
|
||||
className={classNames(className, disabled && 'opacity-disabled')}
|
||||
>
|
||||
<div className="flex items-center justify-center">
|
||||
<Icon size="sm" icon={checked ? 'check' : 'empty'} />
|
||||
<div className="relative flex">
|
||||
<input
|
||||
aria-hidden
|
||||
className="appearance-none w-4 h-4 flex-shrink-0 border border-gray-200 rounded focus:border-focus outline-none ring-0"
|
||||
type="checkbox"
|
||||
disabled={disabled}
|
||||
onChange={() => onChange(!checked)}
|
||||
/>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<Icon size="sm" icon={checked ? 'check' : 'empty'} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{/*<button*/}
|
||||
{/* role="checkbox"*/}
|
||||
{/* aria-checked={checked ? 'true' : 'false'}*/}
|
||||
{/* disabled={disabled}*/}
|
||||
{/* onClick={handleClick}*/}
|
||||
{/* title={title}*/}
|
||||
{/* className={classNames(*/}
|
||||
{/* className,*/}
|
||||
{/* 'flex-shrink-0 w-4 h-4 border border-gray-200 rounded',*/}
|
||||
{/* 'focus:border-focus',*/}
|
||||
{/* 'disabled:opacity-disabled',*/}
|
||||
{/* checked && 'bg-gray-200/10',*/}
|
||||
{/* // Remove focus style*/}
|
||||
{/* 'outline-none',*/}
|
||||
{/* )}*/}
|
||||
{/*>*/}
|
||||
{/*</button>*/}
|
||||
{!hideLabel && title}
|
||||
</HStack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -479,11 +479,6 @@ interface MenuItemHotKeyProps {
|
||||
}
|
||||
|
||||
function MenuItemHotKey({ action, onSelect, item }: MenuItemHotKeyProps) {
|
||||
if (action) {
|
||||
console.log('MENU ITEM HOTKEY', action, item);
|
||||
}
|
||||
useHotKey(action ?? null, () => {
|
||||
onSelect(item);
|
||||
});
|
||||
useHotKey(action ?? null, () => onSelect(item));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -290,13 +290,29 @@ function getExtensions({
|
||||
|
||||
// Handle onChange
|
||||
EditorView.updateListener.of((update) => {
|
||||
if (onChange && update.docChanged) {
|
||||
// Only fire onChange if the document changed and the update was from user input. This prevents firing onChange when the document is updated when
|
||||
// changing pages (one request to another in header editor)
|
||||
if (onChange && update.docChanged && isViewUpdateFromUserInput(update)) {
|
||||
onChange.current?.(update.state.doc.toString());
|
||||
}
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
function isViewUpdateFromUserInput(viewUpdate: ViewUpdate) {
|
||||
// Make sure document has changed, ensuring user events like selections don't count.
|
||||
if (viewUpdate.docChanged) {
|
||||
// Check transactions for any that are direct user input, not changes from Y.js or another extension.
|
||||
for (const transaction of viewUpdate.transactions) {
|
||||
// Not using Transaction.isUserEvent because that only checks for a specific User event type ( "input", "delete", etc.). Checking the annotation directly allows for any type of user event.
|
||||
const userEventType = transaction.annotation(Transaction.userEvent);
|
||||
if (userEventType) return userEventType;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const syncGutterBg = ({
|
||||
parent,
|
||||
className = '',
|
||||
|
||||
@@ -335,7 +335,8 @@ const FormRow = memo(function FormRow({
|
||||
<span className="w-3" />
|
||||
)}
|
||||
<Checkbox
|
||||
title={pairContainer.pair.enabled ? 'disable entry' : 'Enable item'}
|
||||
hideLabel
|
||||
title={pairContainer.pair.enabled ? 'Disable item' : 'Enable item'}
|
||||
disabled={isLast}
|
||||
checked={isLast ? false : !!pairContainer.pair.enabled}
|
||||
className={classNames('mr-2', isLast && '!opacity-disabled')}
|
||||
|
||||
@@ -54,7 +54,7 @@ export const VStack = forwardRef(function VStack(
|
||||
});
|
||||
|
||||
type BaseStackProps = HTMLAttributes<HTMLElement> & {
|
||||
as?: ComponentType | 'ul' | 'form';
|
||||
as?: ComponentType | 'ul' | 'label' | 'form';
|
||||
space?: keyof typeof gapClasses;
|
||||
alignItems?: 'start' | 'center' | 'stretch';
|
||||
justifyContent?: 'start' | 'center' | 'end' | 'between';
|
||||
|
||||
Reference in New Issue
Block a user