浏览代码

Show search result.

oldj 4 年之前
父节点
当前提交
01afcb6047

+ 87 - 0
package-lock.json

@@ -37,6 +37,8 @@
         "@types/node": "^14.14.35",
         "@types/node": "^14.14.35",
         "@types/react": "^17.0.3",
         "@types/react": "^17.0.3",
         "@types/react-dom": "^17.0.3",
         "@types/react-dom": "^17.0.3",
+        "@types/react-virtualized-auto-sizer": "^1.0.0",
+        "@types/react-window": "^1.8.3",
         "@types/uuid": "^8.3.0",
         "@types/uuid": "^8.3.0",
         "@umijs/preset-react": "1.x",
         "@umijs/preset-react": "1.x",
         "@umijs/test": "^3.4.6",
         "@umijs/test": "^3.4.6",
@@ -62,6 +64,8 @@
         "react": "^16.8.6",
         "react": "^16.8.6",
         "react-dom": "^16.8.6",
         "react-dom": "^16.8.6",
         "react-icons": "^4.2.0",
         "react-icons": "^4.2.0",
+        "react-virtualized-auto-sizer": "^1.0.5",
+        "react-window": "^1.8.6",
         "smooth-scroll-into-view-if-needed": "^1.1.32",
         "smooth-scroll-into-view-if-needed": "^1.1.32",
         "ts-node": "^9.1.1",
         "ts-node": "^9.1.1",
         "tsconfig-paths-webpack-plugin": "^3.5.1",
         "tsconfig-paths-webpack-plugin": "^3.5.1",
@@ -3876,6 +3880,24 @@
         "@types/react-router": "*"
         "@types/react-router": "*"
       }
       }
     },
     },
+    "node_modules/@types/react-virtualized-auto-sizer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.0.tgz",
+      "integrity": "sha512-NMErdIdSnm2j/7IqMteRiRvRulpjoELnXWUwdbucYCz84xG9PHcoOrr7QfXwB/ku7wd6egiKFrzt/+QK4Imeeg==",
+      "dev": true,
+      "dependencies": {
+        "@types/react": "*"
+      }
+    },
+    "node_modules/@types/react-window": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.3.tgz",
+      "integrity": "sha512-Xf+IR2Zyiyh/6z1CM8kv1aQba3S3X/hBXt4tH+T9bDSIGwFhle0GZFZGTSU8nw2cUT3UNbCHDjhxVQVZPtE8cA==",
+      "dev": true,
+      "dependencies": {
+        "@types/react": "*"
+      }
+    },
     "node_modules/@types/scheduler": {
     "node_modules/@types/scheduler": {
       "version": "0.16.1",
       "version": "0.16.1",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz",
@@ -18970,6 +18992,36 @@
         "tween-functions": "^1.0.1"
         "tween-functions": "^1.0.1"
       }
       }
     },
     },
+    "node_modules/react-virtualized-auto-sizer": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.5.tgz",
+      "integrity": "sha512-kivjYVWX15TX2IUrm8F1jaCEX8EXrpy3DD+u41WGqJ1ZqbljWpiwscV+VxOM1l7sSIM1jwi2LADjhhAJkJ9dxA==",
+      "dev": true,
+      "engines": {
+        "node": ">8.0.0"
+      },
+      "peerDependencies": {
+        "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0",
+        "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0"
+      }
+    },
+    "node_modules/react-window": {
+      "version": "1.8.6",
+      "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.6.tgz",
+      "integrity": "sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/runtime": "^7.0.0",
+        "memoize-one": ">=3.1.1 <6"
+      },
+      "engines": {
+        "node": ">8.0.0"
+      },
+      "peerDependencies": {
+        "react": "^15.0.0 || ^16.0.0 || ^17.0.0",
+        "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0"
+      }
+    },
     "node_modules/read-config-file": {
     "node_modules/read-config-file": {
       "version": "6.0.0",
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.0.0.tgz",
       "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.0.0.tgz",
@@ -26668,6 +26720,24 @@
         "@types/react-router": "*"
         "@types/react-router": "*"
       }
       }
     },
     },
+    "@types/react-virtualized-auto-sizer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.0.tgz",
+      "integrity": "sha512-NMErdIdSnm2j/7IqMteRiRvRulpjoELnXWUwdbucYCz84xG9PHcoOrr7QfXwB/ku7wd6egiKFrzt/+QK4Imeeg==",
+      "dev": true,
+      "requires": {
+        "@types/react": "*"
+      }
+    },
+    "@types/react-window": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.3.tgz",
+      "integrity": "sha512-Xf+IR2Zyiyh/6z1CM8kv1aQba3S3X/hBXt4tH+T9bDSIGwFhle0GZFZGTSU8nw2cUT3UNbCHDjhxVQVZPtE8cA==",
+      "dev": true,
+      "requires": {
+        "@types/react": "*"
+      }
+    },
     "@types/scheduler": {
     "@types/scheduler": {
       "version": "0.16.1",
       "version": "0.16.1",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz",
@@ -38720,6 +38790,23 @@
         "tween-functions": "^1.0.1"
         "tween-functions": "^1.0.1"
       }
       }
     },
     },
+    "react-virtualized-auto-sizer": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.5.tgz",
+      "integrity": "sha512-kivjYVWX15TX2IUrm8F1jaCEX8EXrpy3DD+u41WGqJ1ZqbljWpiwscV+VxOM1l7sSIM1jwi2LADjhhAJkJ9dxA==",
+      "dev": true,
+      "requires": {}
+    },
+    "react-window": {
+      "version": "1.8.6",
+      "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.6.tgz",
+      "integrity": "sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==",
+      "dev": true,
+      "requires": {
+        "@babel/runtime": "^7.0.0",
+        "memoize-one": ">=3.1.1 <6"
+      }
+    },
     "read-config-file": {
     "read-config-file": {
       "version": "6.0.0",
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.0.0.tgz",
       "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.0.0.tgz",

+ 4 - 0
package.json

@@ -47,6 +47,8 @@
     "@types/node": "^14.14.35",
     "@types/node": "^14.14.35",
     "@types/react": "^17.0.3",
     "@types/react": "^17.0.3",
     "@types/react-dom": "^17.0.3",
     "@types/react-dom": "^17.0.3",
+    "@types/react-virtualized-auto-sizer": "^1.0.0",
+    "@types/react-window": "^1.8.3",
     "@types/uuid": "^8.3.0",
     "@types/uuid": "^8.3.0",
     "@umijs/preset-react": "1.x",
     "@umijs/preset-react": "1.x",
     "@umijs/test": "^3.4.6",
     "@umijs/test": "^3.4.6",
@@ -72,6 +74,8 @@
     "react": "^16.8.6",
     "react": "^16.8.6",
     "react-dom": "^16.8.6",
     "react-dom": "^16.8.6",
     "react-icons": "^4.2.0",
     "react-icons": "^4.2.0",
+    "react-virtualized-auto-sizer": "^1.0.5",
+    "react-window": "^1.8.6",
     "smooth-scroll-into-view-if-needed": "^1.1.32",
     "smooth-scroll-into-view-if-needed": "^1.1.32",
     "ts-node": "^9.1.1",
     "ts-node": "^9.1.1",
     "tsconfig-paths-webpack-plugin": "^3.5.1",
     "tsconfig-paths-webpack-plugin": "^3.5.1",

+ 4 - 0
src/common/i18n/languages/en.ts

@@ -74,6 +74,8 @@ export default {
   import_fail: 'Import failed!',
   import_fail: 'Import failed!',
   import_from_url: 'Import from URL',
   import_from_url: 'Import from URL',
   is_latest_version_inform: 'Great, you are running the latest version!',
   is_latest_version_inform: 'Great, you are running the latest version!',
+  item_found: '{0} item found.',
+  items_found: '{0} items found.',
   language: 'Language',
   language: 'Language',
   last_refresh: 'Last refresh: ',
   last_refresh: 'Last refresh: ',
   latest_version_desc: 'The latest version is: {0}',
   latest_version_desc: 'The latest version is: {0}',
@@ -81,6 +83,7 @@ export default {
   lines: 'lines',
   lines: 'lines',
   loading: 'Loading...',
   loading: 'Loading...',
   local: 'Local',
   local: 'Local',
+  match: 'Match',
   migrate_confirm: 'SwitchHosts v4.0 uses a new data storage format, do you want to migrate old data to the new format?',
   migrate_confirm: 'SwitchHosts v4.0 uses a new data storage format, do you want to migrate old data to the new format?',
   migrate_data: 'Migrate data',
   migrate_data: 'Migrate data',
   minimize: 'Minimize',
   minimize: 'Minimize',
@@ -130,6 +133,7 @@ export default {
   theme: 'Theme',
   theme: 'Theme',
   theme_dark: 'Dark',
   theme_dark: 'Dark',
   theme_light: 'Light',
   theme_light: 'Light',
+  title: 'Title',
   toggle_developer_tools: 'Toggle Developer Tools',
   toggle_developer_tools: 'Toggle Developer Tools',
   toggle_dock_icon: 'Toggle the dock icon',
   toggle_dock_icon: 'Toggle the dock icon',
   toggle_full_screen: 'Toggle full screen',
   toggle_full_screen: 'Toggle full screen',

+ 4 - 0
src/common/i18n/languages/zh.ts

@@ -76,6 +76,8 @@ const lang: LanguageDict = {
   import_fail: '导入失败!',
   import_fail: '导入失败!',
   import_from_url: '从 URL 导入',
   import_from_url: '从 URL 导入',
   is_latest_version_inform: '太棒了,你正在运行的是最新版本!',
   is_latest_version_inform: '太棒了,你正在运行的是最新版本!',
+  item_found: '{0} 项匹配',
+  items_found: '{0} 项匹配',
   language: '语言',
   language: '语言',
   last_refresh: '最后刷新:',
   last_refresh: '最后刷新:',
   latest_version_desc: '最新的版本为:{0}',
   latest_version_desc: '最新的版本为:{0}',
@@ -83,6 +85,7 @@ const lang: LanguageDict = {
   lines: '行',
   lines: '行',
   loading: '加载中...',
   loading: '加载中...',
   local: '本地',
   local: '本地',
+  match: '匹配',
   migrate_confirm: 'SwitchHosts v4.0 使用了新的数据存储格式,是否迁移旧数据为新格式?',
   migrate_confirm: 'SwitchHosts v4.0 使用了新的数据存储格式,是否迁移旧数据为新格式?',
   migrate_data: '迁移数据',
   migrate_data: '迁移数据',
   minimize: '最小化',
   minimize: '最小化',
@@ -132,6 +135,7 @@ const lang: LanguageDict = {
   theme: '主题',
   theme: '主题',
   theme_dark: '夜间',
   theme_dark: '夜间',
   theme_light: '明亮',
   theme_light: '明亮',
+  title: '标题',
   toggle_developer_tools: '切换开发者工具',
   toggle_developer_tools: '切换开发者工具',
   toggle_dock_icon: '显示/隐藏任务栏图标',
   toggle_dock_icon: '显示/隐藏任务栏图标',
   toggle_full_screen: '切换全屏',
   toggle_full_screen: '切换全屏',

+ 45 - 0
src/renderer/pages/find.less

@@ -4,3 +4,48 @@
   width: 100vw;
   width: 100vw;
   height: 100vh;
   height: 100vh;
 }
 }
+
+.result_row {
+  border-bottom: 1px solid var(--swh-border-color-0);
+  display: grid;
+  grid-template-columns: 1fr 20% 60px;
+  grid-column-gap: 4px;
+  line-height: 28px;
+  padding-left: 8px;
+}
+
+.result_content {
+  //display: flex;
+
+  .code;
+  .ell;
+
+  span {
+    //display: inline-block;
+  }
+
+  span:first-child {
+    //max-width: 40%;
+    //.ell;
+  }
+}
+
+.result_title {
+  display: flex;
+  align-items: center;
+  overflow: hidden;
+
+  span {
+    display: inline-block;
+    margin-left: 4px;
+    .ell;
+  }
+}
+
+.result_line {
+  color: var(--swh-font-color-weak);
+}
+
+.highlight {
+  background: var(--swh-highlight-bg);
+}

+ 55 - 5
src/renderer/pages/find.tsx

@@ -12,9 +12,11 @@ import {
   Input,
   Input,
   InputGroup,
   InputGroup,
   InputLeftElement,
   InputLeftElement,
+  Spacer,
   useColorMode,
   useColorMode,
   VStack,
   VStack,
 } from '@chakra-ui/react'
 } from '@chakra-ui/react'
+import ItemIcon from '@renderer/components/ItemIcon'
 import { actions, agent } from '@renderer/core/agent'
 import { actions, agent } from '@renderer/core/agent'
 import useOnBroadcast from '@renderer/core/useOnBroadcast'
 import useOnBroadcast from '@renderer/core/useOnBroadcast'
 import { IFindResultItem } from '@root/common/types'
 import { IFindResultItem } from '@root/common/types'
@@ -22,6 +24,8 @@ import { useDebounce } from 'ahooks'
 import lodash from 'lodash'
 import lodash from 'lodash'
 import React, { useEffect, useRef, useState } from 'react'
 import React, { useEffect, useRef, useState } from 'react'
 import { IoSearch } from 'react-icons/io5'
 import { IoSearch } from 'react-icons/io5'
+import { FixedSizeList as List, ListChildComponentProps } from 'react-window'
+import AutoSizer from 'react-virtualized-auto-sizer'
 import styles from './find.less'
 import styles from './find.less'
 
 
 interface Props {
 interface Props {
@@ -29,10 +33,10 @@ interface Props {
 }
 }
 
 
 const find = (props: Props) => {
 const find = (props: Props) => {
-  const { lang, setLocale } = useModel('useI18n')
+  const { lang, i18n, setLocale } = useModel('useI18n')
   const { configs, loadConfigs } = useModel('useConfigs')
   const { configs, loadConfigs } = useModel('useConfigs')
   const { colorMode, setColorMode } = useColorMode()
   const { colorMode, setColorMode } = useColorMode()
-  const [keyword, setKeyword] = useState('')
+  const [keyword, setKeyword] = useState('ato')
   const [replact_to, setReplaceTo] = useState('')
   const [replact_to, setReplaceTo] = useState('')
   const [is_regexp, setIsRegExp] = useState(false)
   const [is_regexp, setIsRegExp] = useState(false)
   const [is_ignore_case, setIsIgnoreCase] = useState(false)
   const [is_ignore_case, setIsIgnoreCase] = useState(false)
@@ -94,9 +98,31 @@ const find = (props: Props) => {
       is_ignore_case,
       is_ignore_case,
     })
     })
     setFindResult(result)
     setFindResult(result)
-    console.log(result)
   }, 500)
   }, 500)
 
 
+  const ResultRow = (row_data: ListChildComponentProps) => {
+    let data = find_result[row_data.index]
+    return (
+      <Box
+        style={row_data.style}
+        className={styles.result_row}
+        borderBottomWidth={1}
+        borderBottomColor={configs?.theme === 'dark' ? 'gray.600' : 'gray.200'}
+      >
+        <div className={styles.result_content}>
+          <span>{data.before}</span>
+          <span className={styles.highlight}>{data.match}</span>
+          <span>{data.after}</span>
+        </div>
+        <div className={styles.result_title}>
+          <ItemIcon type={data.item_type}/>
+          <span>{data.item_title}</span>
+        </div>
+        <div className={styles.result_line}>{data.line}</div>
+      </Box>
+    )
+  }
+
   return (
   return (
     <div className={styles.root}>
     <div className={styles.root}>
       <VStack
       <VStack
@@ -152,12 +178,34 @@ const find = (props: Props) => {
           >{lang.ignore_case}</Checkbox>
           >{lang.ignore_case}</Checkbox>
         </HStack>
         </HStack>
 
 
+        <Box
+          w="100%"
+          borderTopWidth={1}
+        >
+          <div className={styles.result_row}>
+            <div>{lang.match}</div>
+            <div>{lang.title}</div>
+            <div>{lang.line}</div>
+          </div>
+        </Box>
+
         <Box
         <Box
           w="100%"
           w="100%"
           flex="1"
           flex="1"
           bgColor={configs?.theme === 'dark' ? 'gray.700' : 'gray.100'}
           bgColor={configs?.theme === 'dark' ? 'gray.700' : 'gray.100'}
         >
         >
-          result
+          <AutoSizer>
+            {({ width, height }) => (
+              <List
+                width={width}
+                height={height}
+                itemCount={find_result.length}
+                itemSize={28}
+              >
+                {ResultRow}
+              </List>
+            )}
+          </AutoSizer>
         </Box>
         </Box>
 
 
         <HStack
         <HStack
@@ -165,8 +213,10 @@ const find = (props: Props) => {
           py={2}
           py={2}
           px={4}
           px={4}
           spacing={4}
           spacing={4}
-          justifyContent="flex-end"
+          // justifyContent="flex-end"
         >
         >
+          <span>{i18n.trans(find_result.length > 1 ? 'items_found' : 'item_found', [find_result.length.toString()])}</span>
+          <Spacer/>
           <Button
           <Button
             size="sm"
             size="sm"
             variant="outline"
             variant="outline"

+ 6 - 0
src/renderer/styles/fn.less

@@ -4,3 +4,9 @@
 .code {
 .code {
   font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
   font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
 }
 }
+
+.ell {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}

+ 1 - 0
src/renderer/styles/themes/dark.less

@@ -7,6 +7,7 @@
   --swh-font-color-weak: #999;
   --swh-font-color-weak: #999;
   --swh-font-color-reverse: #a7a7a7;
   --swh-font-color-reverse: #a7a7a7;
   --swh-border-radius: 4px;
   --swh-border-radius: 4px;
+  --swh-highlight-bg: #993;
 
 
   //  top bar
   //  top bar
   --swh-top-bar-height: 40px;
   --swh-top-bar-height: 40px;

+ 1 - 0
src/renderer/styles/themes/light.less

@@ -7,6 +7,7 @@
   --swh-font-color-weak: #999;
   --swh-font-color-weak: #999;
   --swh-font-color-reverse: #fff;
   --swh-font-color-reverse: #fff;
   --swh-border-radius: 4px;
   --swh-border-radius: 4px;
+  --swh-highlight-bg: #ee0;
 
 
   //  top bar
   //  top bar
   --swh-top-bar-height: 40px;
   --swh-top-bar-height: 40px;