From 725f130e4280f5ef2196b6cac79086edfb2f704a Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sat, 10 Jun 2023 00:45:41 +0100 Subject: [PATCH] feat: responsive settings ui --- src/features/settings/Settings.tsx | 105 ++++++++-- src/features/settings/panels/PanelQMC.tsx | 153 --------------- .../settings/panels/PanelQMCv2Key.tsx | 183 ++++++++++++++++++ src/features/settings/settingsSlice.ts | 5 +- src/tabs/SettingsTab.tsx | 9 +- src/themes/Tabs.tsx | 10 + 6 files changed, 294 insertions(+), 171 deletions(-) delete mode 100644 src/features/settings/panels/PanelQMC.tsx create mode 100644 src/features/settings/panels/PanelQMCv2Key.tsx diff --git a/src/features/settings/Settings.tsx b/src/features/settings/Settings.tsx index 9681cf9..55ee431 100644 --- a/src/features/settings/Settings.tsx +++ b/src/features/settings/Settings.tsx @@ -1,20 +1,95 @@ -import { Tab, TabList, TabPanel, TabPanels, Tabs, Text } from '@chakra-ui/react'; -import { PanelQMC } from './panels/PanelQMC'; +import { + Button, + Flex, + Menu, + MenuButton, + MenuItem, + MenuList, + Portal, + Tab, + TabList, + TabPanel, + TabPanels, + Tabs, + Text, + useBreakpointValue, +} from '@chakra-ui/react'; +import { PanelQMCv2Key } from './panels/PanelQMCv2Key'; +import { useState } from 'react'; +import { MdExpandMore, MdMenu } from 'react-icons/md'; + +const TABS: { name: string; Tab: () => JSX.Element }[] = [ + { name: 'QMCv2 密钥', Tab: PanelQMCv2Key }, + { + name: '其它/待定', + Tab: () => ( + + 这里空空如也~ + + ), + }, +]; export function Settings() { - return ( - - - QQ 音乐 - 其它 - + const isLargeWidthDevice = + useBreakpointValue({ + base: false, + lg: true, + }) ?? false; - - - - 待定 - - - + const [tabIndex, setTabIndex] = useState(0); + const handleTabChange = (idx: number) => { + setTabIndex(idx); + }; + + return ( + + + } + rightIcon={} + colorScheme="gray" + variant="outline" + w="full" + flexShrink={0} + hidden={isLargeWidthDevice} + mb="4" + > + {TABS[tabIndex].name} + + + + {TABS.map(({ name }, i) => ( + setTabIndex(i)}> + {name} + + ))} + + + + + + + + + {TABS.map(({ name, Tab }) => ( + + ))} + + + ); } diff --git a/src/features/settings/panels/PanelQMC.tsx b/src/features/settings/panels/PanelQMC.tsx deleted file mode 100644 index 932d430..0000000 --- a/src/features/settings/panels/PanelQMC.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import { - Box, - Button, - ButtonGroup, - Flex, - HStack, - Heading, - Icon, - IconButton, - Input, - InputGroup, - InputLeftElement, - InputRightElement, - List, - ListItem, - Menu, - MenuButton, - MenuDivider, - MenuItem, - MenuList, - Spacer, - TabPanel, - Text, - VStack, -} from '@chakra-ui/react'; -import { useSelector } from 'react-redux'; -import { selectQM2CSettings } from '../settingsSlice'; -import React, { useEffect, useState } from 'react'; -import { nanoid } from 'nanoid'; -import { produce } from 'immer'; -import { MdAdd, MdDeleteForever, MdExpandMore, MdFileUpload, MdVpnKey } from 'react-icons/md'; - -interface InternalQMCKeys { - id: string; - name: string; - key: string; -} - -export function PanelQMC() { - const qmcSettings = useSelector(selectQM2CSettings); - const [qmcKeys, setQMCKeys] = useState([]); - const resetQmcKeys = () => { - const result: InternalQMCKeys[] = []; - for (const [name, key] of Object.entries(qmcSettings.keys)) { - result.push({ id: name, name, key }); - } - setQMCKeys(result); - }; - const addRow = () => { - setQMCKeys((prev) => [...prev, { id: nanoid(), key: '', name: '' }]); - }; - const updateKey = (prop: 'name' | 'key', id: string, e: React.ChangeEvent) => { - setQMCKeys((prev) => - produce(prev, (draft) => { - const item = draft.find((item) => item.id === id); - if (item) { - item[prop] = e.target.value; - } - }) - ); - }; - const applyChanges = () => { - // - }; - const clearAll = () => setQMCKeys([]); - useEffect(resetQmcKeys, [qmcSettings.keys]); - - return ( - - - - 密钥 - - - - - {qmcKeys.map(({ id, key, name }, i) => ( - - - - {i + 1} - - - - updateKey('name', id, e)} - /> - - - - - - updateKey('key', id, e)} - /> - - - {key.length || '?'} - - - - - - - ))} - - {qmcKeys.length === 0 && 还没有添加密钥。} - - - - - 密钥填充完毕后,按下「应用」来使用新的设置。 - - - - - - }> - 1 - - - alert('TODO!')} icon={}> - 从文件导入 (JSON) - - - }> - 清空 - - - - - - - - - - - - - - ); -} diff --git a/src/features/settings/panels/PanelQMCv2Key.tsx b/src/features/settings/panels/PanelQMCv2Key.tsx new file mode 100644 index 0000000..1be0f5b --- /dev/null +++ b/src/features/settings/panels/PanelQMCv2Key.tsx @@ -0,0 +1,183 @@ +import { + Box, + Button, + ButtonGroup, + Center, + Flex, + HStack, + Heading, + Icon, + IconButton, + Input, + InputGroup, + InputLeftElement, + InputRightElement, + List, + ListItem, + Menu, + MenuButton, + MenuDivider, + MenuItem, + MenuList, + Spacer, + TabPanel, + Text, + VStack, + useToast, +} from '@chakra-ui/react'; +import { useDispatch, useSelector } from 'react-redux'; +import { selectQM2CSettings, updateQMC2Keys } from '../settingsSlice'; +import React, { useEffect, useState } from 'react'; +import { nanoid } from 'nanoid'; +import { produce } from 'immer'; +import { MdAdd, MdAndroid, MdDeleteForever, MdExpandMore, MdFileUpload, MdVpnKey } from 'react-icons/md'; +import { objectify } from 'radash'; + +interface InternalQMCKeys { + id: string; + name: string; + key: string; +} + +export function PanelQMCv2Key() { + const toast = useToast(); + const dispatch = useDispatch(); + const qmcSettings = useSelector(selectQM2CSettings); + const [isModified, setIsModified] = useState(false); + const [qmcKeys, setQMCKeys] = useState([]); + const resetQmcKeys = () => { + const result: InternalQMCKeys[] = []; + for (const [name, key] of Object.entries(qmcSettings.keys)) { + result.push({ id: name, name, key }); + } + setQMCKeys(result); + }; + const addRow = () => { + setIsModified(true); + setQMCKeys((prev) => [...prev, { id: nanoid(), key: '', name: '' }]); + }; + const updateKey = (prop: 'name' | 'key', id: string, e: React.ChangeEvent) => { + setIsModified(true); + setQMCKeys((prev) => + produce(prev, (draft) => { + const item = draft.find((item) => item.id === id); + if (item) { + item[prop] = e.target.value; + } + }) + ); + }; + const applyChanges = () => { + dispatch( + updateQMC2Keys( + objectify( + qmcKeys, + (item) => item.name, + (item) => item.key + ) + ) + ); + + toast({ + title: 'QMCv2 密钥的更改已保存。', + status: 'success', + isClosable: true, + duration: 2500, + }); + }; + const clearAll = () => setQMCKeys([]); + useEffect(resetQmcKeys, [qmcSettings.keys]); + + return ( + + + 密钥 + + + + + + + }> + + alert('TODO!')} icon={}> + 从文件导入(JSON) + + {/* 需要加入 SQL.js 再处理 */} + + + }> + 清空 + + + + + + + + + {qmcKeys.map(({ id, key, name }, i) => ( + + + + {i + 1} + + + + updateKey('name', id, e)} + /> + + + + + + updateKey('key', id, e)} /> + + + {key.length || '?'} + + + + + + + ))} + + {qmcKeys.length === 0 && 还没有添加密钥。} + + + + +
+ + 重复项只保留最后一项。 + +
+ + + + + +
+
+
+ ); +} diff --git a/src/features/settings/settingsSlice.ts b/src/features/settings/settingsSlice.ts index c215886..2891840 100644 --- a/src/features/settings/settingsSlice.ts +++ b/src/features/settings/settingsSlice.ts @@ -21,13 +21,16 @@ export const settingsSlice = createSlice({ updateSettings: (_state, { payload }: PayloadAction) => { return payload; }, + updateQMC2Keys: (state, { payload }: PayloadAction) => { + state.qmc2.keys = payload; + }, resetConfig: () => { return initialState; }, }, }); -export const { updateSettings, resetConfig } = settingsSlice.actions; +export const { updateSettings, resetConfig, updateQMC2Keys } = settingsSlice.actions; export const selectQM2CSettings = (state: RootState) => state.settings.qmc2; diff --git a/src/tabs/SettingsTab.tsx b/src/tabs/SettingsTab.tsx index c105ed6..2642feb 100644 --- a/src/tabs/SettingsTab.tsx +++ b/src/tabs/SettingsTab.tsx @@ -1,9 +1,14 @@ -import { Container, Flex } from '@chakra-ui/react'; +import { Container, Flex, useBreakpointValue } from '@chakra-ui/react'; import { Settings } from '~/features/settings/Settings'; export function SettingsTab() { + const containerProps = useBreakpointValue({ + base: { p: '0' }, + lg: { p: undefined }, + }); + return ( - + ); diff --git a/src/themes/Tabs.tsx b/src/themes/Tabs.tsx index ac78b2d..e13a42b 100644 --- a/src/themes/Tabs.tsx +++ b/src/themes/Tabs.tsx @@ -17,6 +17,13 @@ const variantLineInvert = definePartsStyle((props) => { [borderProp]: '2px solid', borderColor: 'inherit', }, + tabpanels: { + flex: 1, + minH: 0, + }, + tabpanel: { + padding: 0, + }, tab: { [borderProp]: '2px solid', borderColor: 'transparent', @@ -42,7 +49,10 @@ const variantLineInvert = definePartsStyle((props) => { bg: $bg.reference, }, root: { + display: 'flex', + flexDir: isVertical ? 'row' : 'column', gap: 8, + minH: 0, }, }; });