diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 63358f8c..1b16baf5 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,31 +1,31 @@ module.exports = { - extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:import/recommended', - 'plugin:jsx-a11y/recommended', - 'plugin:@typescript-eslint/recommended', - 'eslint-config-prettier', - ], - ignorePatterns: ['src-tauri/**/*'], - settings: { - react: { - version: 'detect', - }, - 'import/resolver': { - node: { - paths: ['src-web'], - extensions: ['.js', '.jsx', '.ts', '.tsx'], - }, - }, - }, - rules: { - "jsx-a11y/no-autofocus": "warn", - "react/react-in-jsx-scope": "off", - "@typescript-eslint/consistent-type-imports": ["error", { - prefer: "type-imports", - disallowTypeAnnotations: true, - fixStyle: "separate-type-imports", - }] + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:import/recommended", + "plugin:jsx-a11y/recommended", + "plugin:@typescript-eslint/recommended", + "eslint-config-prettier" + ], + ignorePatterns: ["src-tauri/**/*"], + settings: { + react: { + version: "detect" }, + "import/resolver": { + node: { + paths: ["src-web"], + extensions: [".js", ".jsx", ".ts", ".tsx"] + } + } + }, + rules: { + "jsx-a11y/no-autofocus": "warn", + "react/react-in-jsx-scope": "off", + "@typescript-eslint/consistent-type-imports": ["error", { + prefer: "type-imports", + disallowTypeAnnotations: true, + fixStyle: "separate-type-imports" + }], + } }; diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns index 0c169d5f..ed1d9efd 100644 Binary files a/src-tauri/icons/icon.icns and b/src-tauri/icons/icon.icns differ diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx index e7196e73..21d8c662 100644 --- a/src-web/components/Sidebar.tsx +++ b/src-web/components/Sidebar.tsx @@ -1,8 +1,9 @@ import classnames from 'classnames'; -import { useState } from 'react'; +import React, { useRef, useState } from 'react'; import { useActiveRequest } from '../hooks/useActiveRequest'; import { useCreateRequest } from '../hooks/useCreateRequest'; import { useDeleteRequest } from '../hooks/useDeleteRequest'; +import { useKeyValues } from '../hooks/useKeyValues'; import { useRequests } from '../hooks/useRequests'; import { useTheme } from '../hooks/useTheme'; import { useUpdateRequest } from '../hooks/useUpdateRequest'; @@ -16,18 +17,65 @@ interface Props { className?: string; } +const MIN_WIDTH = 130; + export function Sidebar({ className }: Props) { + const [isDragging, setIsDragging] = useState(false); + const width = useKeyValues({ key: 'sidebar_width', initialValue: 200 }); const requests = useRequests(); const activeRequest = useActiveRequest(); const createRequest = useCreateRequest({ navigateAfter: true }); const { appearance, toggleAppearance } = useTheme(); + + const moveState = useRef<{ move: (e: MouseEvent) => void; up: () => void } | null>(null); + const unsub = () => { + if (moveState.current !== null) { + document.documentElement.removeEventListener('mousemove', moveState.current.move); + document.documentElement.removeEventListener('mouseup', moveState.current.up); + } + }; + + const handleResizeStart = (e: React.MouseEvent) => { + unsub(); + const mouseStartX = e.clientX; + const startWidth = width.value; + moveState.current = { + move: (e: MouseEvent) => { + const newWidth = Math.max(MIN_WIDTH, startWidth + (e.clientX - mouseStartX)); + width.set(newWidth); + }, + up: () => { + unsub(); + setIsDragging(false); + }, + }; + document.documentElement.addEventListener('mousemove', moveState.current.move); + document.documentElement.addEventListener('mouseup', moveState.current.up); + setIsDragging(true); + }; + return (
+ {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */} +
+
+
({ namespace = DEFAULT_NAMESPACE, key, initialValue, }: { namespace?: string; key: string | string[]; - initialValue: string; + initialValue: T; }) { const query = useQuery({ initialData: null, @@ -29,15 +29,25 @@ export function useKeyValues({ queryFn: async () => invoke('get_key_value', { namespace, key: buildKey(key) }), }); - const mutate = useMutation({ + const mutate = useMutation({ mutationFn: (value) => { - return invoke('set_key_value', { namespace, key: buildKey(key), value }); + return invoke('set_key_value', { + namespace, + key: buildKey(key), + value: JSON.stringify(value), + }); }, }); + let value: T; + try { + value = JSON.parse(query.data?.value ?? JSON.stringify(initialValue)); + } catch (e) { + value = initialValue; + } return { - value: query.data?.value ?? initialValue, - set: (value: string) => mutate.mutate(value), + value, + set: (value: T) => mutate.mutate(value), }; }