Skip to content

Commit 6c24d61

Browse files
authored
feat: keyboard table (#210)
2 parents 03f9da2 + 61e0a7e commit 6c24d61

File tree

16 files changed

+350
-52
lines changed

16 files changed

+350
-52
lines changed

apps/linebyline/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"react-router-dom": "^6.9.0",
3434
"remixicon": "^3.4.0",
3535
"styled-components": "^5.3.11",
36+
"tinykeys": "^2.1.0",
3637
"zustand": "^4.3.9"
3738
},
3839
"devDependencies": {

apps/linebyline/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import { Route, Routes } from 'react-router-dom'
88
import 'remixicon/fonts/remixicon.css'
99
import { ThemeProvider } from 'styled-components'
1010
import { GlobalStyles } from './globalStyles'
11-
import { useGlobalSettingData, useGlobalTheme } from './hooks'
12-
import useGlobalOSInfo from './hooks/useOSInfo'
11+
import { useGlobalSettingData, useGlobalTheme, useGlobalKeyboard, useGlobalOSInfo } from './hooks'
1312
import { i18nInit } from './i18n'
1413
import { Root, Setting } from '@/router'
1514
import { loadTask, use } from '@/helper/schedule'
@@ -20,6 +19,7 @@ import { getFileObject, getFileObjectByPath } from './helper/files'
2019

2120
function App() {
2221
useGlobalOSInfo()
22+
useGlobalKeyboard()
2323
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2424
const [_, handler] = useGlobalSettingData()
2525
const { themeColors, muiTheme, setTheme } = useGlobalTheme()

apps/linebyline/src/components/Explorer/index.tsx

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
import { open } from '@tauri-apps/api/dialog'
21
import classNames from 'classnames'
3-
import dayjs from 'dayjs'
42
import type { FC } from 'react'
53
import { memo, useCallback, useMemo, useState } from 'react'
64
import { useTranslation } from 'react-i18next'
75
import type { ListDataItem } from '../List'
86
import { Container } from './styles'
97
import { useEditorStore } from '@/stores'
10-
import { readDirectory } from '@/helper/filesys'
118
import type { IFile } from '@/helper/filesys'
12-
import { useGlobalCacheData } from '@/hooks'
9+
import { useGlobalCacheData, useOpen } from '@/hooks'
1310
import { CacheManager } from '@/helper'
1411
import { Empty, FileTree, List, Popper } from '@/components'
1512
import styled from 'styled-components'
@@ -21,15 +18,16 @@ const RecentListBottom = styled.div`
2118
text-align: center;
2219
2320
&:hover {
24-
background-color: ${props => props.theme.tipsBgColor};
21+
background-color: ${(props) => props.theme.tipsBgColor};
2522
}
2623
`
2724

2825
const Explorer: FC<ExplorerProps> = (props) => {
2926
const { t } = useTranslation()
30-
const { folderData, activeId, setFolderData, addOpenedFile, setActiveId } = useEditorStore()
27+
const { folderData, activeId, addOpenedFile, setActiveId } = useEditorStore()
3128
const [popperOpen, setPopperOpen] = useState(false)
3229
const [cache] = useGlobalCacheData()
30+
const { openFolderDialog, openFolder } = useOpen()
3331

3432
const handleSelect = (item: IFile) => {
3533
if (item.kind === 'dir') return
@@ -38,32 +36,18 @@ const Explorer: FC<ExplorerProps> = (props) => {
3836
setActiveId(item.id)
3937
}
4038

41-
const openRir = useCallback(async (dir: string) => {
42-
try {
43-
const res = await readDirectory(dir)
44-
CacheManager.writeCache('openFolderHistory', { path: dir, time: dayjs() })
45-
setFolderData(res)
46-
} catch (error) {
47-
console.error('error', error)
48-
}
49-
}, [setFolderData])
50-
5139
const handleClearRecent = () => {
5240
CacheManager.writeCache('openFolderHistory', [])
5341
setPopperOpen(false)
5442
}
5543

56-
const handleOpenDirClick = async () => {
57-
const dir = await open({ directory: true, recursive: true })
58-
59-
if (typeof dir !== 'string') return
60-
openRir(dir)
61-
}
62-
63-
const handleOpenHistoryListItemClick = useCallback((item: ListDataItem) => {
64-
openRir(item.title)
65-
setPopperOpen(false)
66-
}, [openRir])
44+
const handleOpenHistoryListItemClick = useCallback(
45+
(item: ListDataItem) => {
46+
openFolder(item.title)
47+
setPopperOpen(false)
48+
},
49+
[openFolder],
50+
)
6751

6852
const listData = useMemo(
6953
() =>
@@ -79,14 +63,14 @@ const Explorer: FC<ExplorerProps> = (props) => {
7963

8064
return (
8165
<Container className={containerCLs}>
82-
<div className="explorer-header">
66+
<div className='explorer-header'>
8367
<small>EXPLORER</small>
84-
<div className="flex" />
68+
<div className='flex' />
8569
</div>
86-
<div className="h-full w-full overflow-auto">
70+
<div className='h-full w-full overflow-auto'>
8771
{folderData && folderData.length > 1 ? (
8872
<FileTree
89-
className="flex-1"
73+
className='flex-1'
9074
data={folderData}
9175
activeId={activeId}
9276
onSelect={handleSelect}
@@ -95,12 +79,12 @@ const Explorer: FC<ExplorerProps> = (props) => {
9579
<Empty />
9680
)}
9781
</div>
98-
<div className="explorer-bottom">
99-
<small className="flex-1 cursor-pointer" onClick={handleOpenDirClick}>
82+
<div className='explorer-bottom'>
83+
<small className='flex-1 cursor-pointer' onClick={openFolderDialog}>
10084
{t('file.openDir')}
10185
</small>
10286
<Popper
103-
placement="top-end"
87+
placement='top-end'
10488
onClickAway={() => setPopperOpen(false)}
10589
open={popperOpen}
10690
content={
@@ -114,10 +98,12 @@ const Explorer: FC<ExplorerProps> = (props) => {
11498
</>
11599
}
116100
>
117-
{listData.length > 0 ? <i
118-
className="ri-more-2-fill icon-border cursor-pointer"
119-
onClick={() => setPopperOpen(true)}
120-
/> : null}
101+
{listData.length > 0 ? (
102+
<i
103+
className='ri-more-2-fill icon-border cursor-pointer'
104+
onClick={() => setPopperOpen(true)}
105+
/>
106+
) : null}
121107
</Popper>
122108
</div>
123109
</Container>

apps/linebyline/src/helper/cacheManager/settingMap.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const settingMap = {
2525
},
2626
},
2727
},
28+
keyboard: {}
2829
}
2930

3031
export enum SettingKeys {

apps/linebyline/src/hooks/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
export { default as useGlobalCacheData } from './useCacheData'
22
export { default as useGlobalSettingData } from './useSettingData'
33
export { default as useGlobalTheme } from './useTheme'
4+
export { default as useGlobalKeyboard } from './useKeyboard'
5+
export { default as useGlobalOSInfo } from './useOSInfo'
6+
export { default as useOpen } from './useOpen'
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { invoke } from '@tauri-apps/api'
2+
import { createGlobalStore } from 'hox'
3+
import { useEffect, useState } from 'react'
4+
import type { KeyBindingMap } from 'tinykeys'
5+
import { createKeybindingsHandler } from 'tinykeys'
6+
import { useCommandStore } from '@/stores'
7+
8+
const getTinyKeyBinding = (keyMap: string[]) => {
9+
let keyBinding = ''
10+
keyMap.forEach((key, index) => {
11+
if (key === 'mod') {
12+
keyBinding += '$mod'
13+
} else {
14+
keyBinding += key
15+
}
16+
17+
if (index < keyMap.length - 1) {
18+
keyBinding += '+'
19+
}
20+
})
21+
return keyBinding
22+
}
23+
24+
function useKeyboard() {
25+
const [keyboardInfos, setKeyboardInfos] = useState<KeyboardInfo[]>([])
26+
const { execute } = useCommandStore()
27+
28+
useEffect(() => {
29+
invoke<{ cmds: KeyboardInfo[] }>('get_keyboard_infos').then((res) => {
30+
setKeyboardInfos(res.cmds)
31+
})
32+
}, [])
33+
34+
useEffect(() => {
35+
const keybindingMap: KeyBindingMap = {}
36+
37+
keyboardInfos.forEach((keyboardInfo) => {
38+
if (keyboardInfo.key_map.length > 0) {
39+
const keybind = getTinyKeyBinding(keyboardInfo.key_map)
40+
keybindingMap[keybind] = () => {
41+
execute(keyboardInfo.id)
42+
}
43+
}
44+
})
45+
console.log('keybindingMap', keybindingMap)
46+
const handler = createKeybindingsHandler(keybindingMap)
47+
48+
window.addEventListener('keydown', handler)
49+
50+
return () => {
51+
window.removeEventListener('keydown', handler)
52+
}
53+
}, [execute, keyboardInfos])
54+
55+
return {
56+
keyboardInfos,
57+
}
58+
}
59+
60+
const [useGlobalKeyboard] = createGlobalStore(useKeyboard)
61+
62+
export default useGlobalKeyboard
63+
64+
interface KeyboardInfo {
65+
id: string
66+
desc: string
67+
key_map: string[]
68+
}

apps/linebyline/src/hooks/useOpen.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { useCallback, useEffect } from 'react'
2+
import { open } from '@tauri-apps/api/dialog'
3+
import { readDirectory } from '@/helper/filesys'
4+
import { CacheManager } from '@/helper'
5+
import { useCommandStore, useEditorStore } from '@/stores'
6+
import dayjs from 'dayjs'
7+
8+
const useOpen = () => {
9+
const { setFolderData } = useEditorStore()
10+
const { addCommand } = useCommandStore()
11+
12+
const openFolder = useCallback(
13+
async (dir: string) => {
14+
try {
15+
const res = await readDirectory(dir)
16+
CacheManager.writeCache('openFolderHistory', { path: dir, time: dayjs() })
17+
setFolderData(res)
18+
} catch (error) {
19+
console.error('error', error)
20+
}
21+
},
22+
[setFolderData],
23+
)
24+
25+
const openFolderDialog = useCallback(async () => {
26+
const dir = await open({ directory: true, recursive: true })
27+
28+
if (typeof dir !== 'string') return
29+
openFolder(dir)
30+
}, [openFolder])
31+
32+
33+
useEffect(() => {
34+
addCommand({ id: 'app:open_folder', handler: openFolderDialog})
35+
}, [addCommand, openFolderDialog])
36+
37+
return {
38+
openFolderDialog,
39+
openFolder
40+
}
41+
}
42+
43+
export default useOpen
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import Table from '@mui/material/Table'
2+
import TableBody from '@mui/material/TableBody'
3+
import TableCell from '@mui/material/TableCell'
4+
import TableContainer from '@mui/material/TableContainer'
5+
import TableHead from '@mui/material/TableHead'
6+
import TableRow from '@mui/material/TableRow'
7+
import Paper from '@mui/material/Paper'
8+
import { useGlobalKeyboard, useGlobalTheme } from '@/hooks'
9+
10+
export function KeyboardTable() {
11+
const { themeColors } = useGlobalTheme()
12+
const { keyboardInfos } = useGlobalKeyboard()
13+
14+
return (
15+
<TableContainer component={Paper}>
16+
<Table size='small' aria-label='caption table'>
17+
<TableHead
18+
sx={{
19+
backgroundColor: themeColors.tipsBgColor,
20+
}}
21+
>
22+
<TableRow>
23+
<TableCell>Command</TableCell>
24+
<TableCell>Descibe</TableCell>
25+
<TableCell>Keybinding</TableCell>
26+
</TableRow>
27+
</TableHead>
28+
<TableBody>
29+
{keyboardInfos.map((row) => (
30+
<TableRow key={row.id}>
31+
<TableCell>{row.id}</TableCell>
32+
<TableCell>{row.desc}</TableCell>
33+
<TableCell>{row.key_map.join('+')}</TableCell>
34+
</TableRow>
35+
))}
36+
</TableBody>
37+
<caption>Currently, custom shortcut keys are not supported.</caption>
38+
</Table>
39+
</TableContainer>
40+
)
41+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './KeyboardTable'

apps/linebyline/src/router/Setting/index.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import settingMap from '@/helper/cacheManager/settingMap'
77
import Logo from '@/assets/logo.svg'
88
import { invoke } from '@tauri-apps/api'
99
import TitleBar from '@/components/TitleBar'
10+
import { KeyboardTable } from './KeyboardTable'
1011

1112
export interface DialogTitleProps {
1213
children?: ReactNode
@@ -34,6 +35,23 @@ function Setting() {
3435
})
3536
}, [])
3637

38+
const renderCurrentSettingData = () => {
39+
if (curGroupKey === 'keyboard') {
40+
return <KeyboardTable />
41+
}
42+
43+
return curGroupKeys.map((key) => {
44+
return (
45+
<SettingGroup
46+
key={key}
47+
group={curGroup[key]}
48+
groupKey={key}
49+
categoryKey={curGroupKey}
50+
/>
51+
)
52+
})
53+
}
54+
3755
return (
3856
<>
3957
<TitleBar transparent />
@@ -71,16 +89,7 @@ function Setting() {
7189
<div className="conf-path">
7290
<small>Path: {confPath}</small>
7391
</div>
74-
{curGroupKeys.map((key) => {
75-
return (
76-
<SettingGroup
77-
key={key}
78-
group={curGroup[key]}
79-
groupKey={key}
80-
categoryKey={curGroupKey}
81-
/>
82-
)
83-
})}
92+
{renderCurrentSettingData()}
8493
</div>
8594
</Container>
8695
</>

apps/linebyline/src/stores/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { default as useEditorStore } from './useEditorStore'
2+
export { default as useCommandStore } from './useCommandStore'

0 commit comments

Comments
 (0)