feat: TextStats + better searchbar
Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
This commit is contained in:
		
							parent
							
								
									85587beb0d
								
							
						
					
					
						commit
						8c78e8ad77
					
				
							
								
								
									
										27
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/App.vue
									
									
									
									
									
								
							| @ -9,7 +9,7 @@ | ||||
|                 <div v-for="section in items" :key="section.title"> | ||||
|                     <v-subheader class="mt-4 pl-4">{{section.title}}</v-subheader> | ||||
| 
 | ||||
|                     <v-list-item v-for="item in section.child" :key="item.text" :to="item.link"> | ||||
|                     <v-list-item v-for="item in section.child" :key="item.text" :to="item.path"> | ||||
|                         <v-list-item-action> | ||||
|                             <v-icon>{{ item.icon }}</v-icon> | ||||
|                         </v-list-item-action> | ||||
| @ -82,6 +82,7 @@ | ||||
| 
 | ||||
| <script> | ||||
|     import SearchBar from "./components/SearchBar"; | ||||
|     import {toolsComponents} from "./router"; | ||||
| 
 | ||||
|     export default { | ||||
|         props: { | ||||
| @ -91,29 +92,7 @@ | ||||
|         data: () => ({ | ||||
|             appVersion: 'v' + process.env.APPLICATION_VERSION, | ||||
|             drawer: null, | ||||
|             items: [ | ||||
|                 { | ||||
|                     title: 'Crypto', | ||||
|                     child: [ | ||||
|                         {icon: 'fa-key', text: 'Token generator', link: '/token-generator'}, | ||||
|                         {icon: 'fa-font', text: 'Hash text', link: '/hash'}, | ||||
|                         {icon: 'fa-lock', text: 'Cypher/uncypher text', link: '/cypher'}, | ||||
|                     ], | ||||
|                 }, | ||||
|                 { | ||||
|                     title: 'Converter', | ||||
|                     child: [ | ||||
|                         {icon: 'fa-calendar', text: 'Date/Time converter', link: '/date-converter'}, | ||||
|                     ], | ||||
|                 }, | ||||
|                 { | ||||
|                     title: 'Web', | ||||
|                     child: [ | ||||
|                         {icon: 'fa-link', text: 'URL encode/decode', link: '/url-encoder'}, | ||||
|                         {icon: 'fa-file-image-o', text: 'File to Base64', link: '/file-to-base64'}, | ||||
|                     ], | ||||
|                 } | ||||
|             ], | ||||
|             items: toolsComponents | ||||
|         }), | ||||
|         created() { | ||||
|             this.$vuetify.theme.dark = true | ||||
|  | ||||
| @ -6,10 +6,13 @@ | ||||
|             color="white" | ||||
|             hide-details | ||||
|             :items="items" | ||||
|             item-text="component.name" | ||||
|             item-text="text" | ||||
|             item-value="path" | ||||
|             solo-inverted | ||||
|             @change="choose" | ||||
|             :filter="filter" | ||||
|             clearable | ||||
|             cache-items | ||||
|     > | ||||
|         <template v-slot:no-data> | ||||
|             <v-list-item> | ||||
| @ -22,23 +25,36 @@ | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
|     import {toolsComponentsFlat} from '../router' | ||||
| 
 | ||||
|     import {toolsRoutes} from '../router' | ||||
| 
 | ||||
|     console.log(toolsComponentsFlat); | ||||
|     export default { | ||||
|         name: "SearchBar", | ||||
|         data(){ | ||||
|         data() { | ||||
|             const vm = this; | ||||
|             return { | ||||
|                 items:toolsRoutes, | ||||
|                 choose(path){ | ||||
|                     vm.$router.push(path) | ||||
|                 items: toolsComponentsFlat, | ||||
|                 choose(path) { | ||||
|                     vm.$router.push(path).catch(() => { | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         methods: { | ||||
|             filter(item, queryText, itemText) { | ||||
|                 const query = queryText.trim().toLowerCase(); | ||||
|                 const nameContainsText = itemText.toLowerCase().includes(query); | ||||
|                 const keywordContainsText = item.keywords ? item.keywords.some(keyword => keyword.toLowerCase().includes(query)) : false; | ||||
|                 return nameContainsText || keywordContainsText; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| <style scoped lang="less"> | ||||
|     ::v-deep .v-list-item__mask{ | ||||
|         color: inherit !important; | ||||
|         background: inherit !important; | ||||
|     } | ||||
| 
 | ||||
| </style> | ||||
| @ -7,38 +7,81 @@ import DateConverter from "./routes/tools/DateConverter"; | ||||
| import UrlEncoder from "./routes/tools/UrlEncoder"; | ||||
| import FileToBase64 from "./routes/tools/FileToBase64"; | ||||
| import TextCypher from "./routes/tools/TextCypher"; | ||||
| import TextStats from "./routes/tools/TextStats"; | ||||
| 
 | ||||
| Vue.use(VueRouter) | ||||
| 
 | ||||
| const toolsRoutes = [ | ||||
| 
 | ||||
| const toolsComponents = [ | ||||
|     { | ||||
|         path: '/token-generator', | ||||
|         component: TokenGenerator | ||||
|         title: 'Crypto', | ||||
|         child: [ | ||||
|             { | ||||
|                 icon: 'fa-key', | ||||
|                 text: 'Token generator', | ||||
|                 path: '/token-generator', | ||||
|                 component: TokenGenerator, | ||||
|                 keywords: ['md5'] | ||||
|             }, | ||||
|             { | ||||
|                 icon: 'fa-font', | ||||
|                 text: 'Hash text', | ||||
|                 path: '/hash', | ||||
|                 component: Hash | ||||
|             }, | ||||
|             { | ||||
|                 icon: 'fa-lock', | ||||
|                 text: 'Cypher/uncypher text', | ||||
|                 path: '/cypher', | ||||
|                 component: TextCypher | ||||
|             }, | ||||
|         ], | ||||
|     }, | ||||
|     { | ||||
|         path: '/hash', | ||||
|         component: Hash | ||||
|         title: 'Converter', | ||||
|         child: [ | ||||
|             { | ||||
|                 icon: 'fa-calendar', | ||||
|                 text: 'Date/Time converter', | ||||
|                 path: '/date-converter', | ||||
|                 component: DateConverter | ||||
|             }, | ||||
|         ], | ||||
|     }, | ||||
|     { | ||||
|         path: '/date-converter', | ||||
|         component: DateConverter | ||||
|         title: 'Web', | ||||
|         child: [ | ||||
|             { | ||||
|                 icon: 'fa-link', | ||||
|                 text: 'URL encode/decode', | ||||
|                 path: '/url-encoder', | ||||
|                 component: UrlEncoder | ||||
|             }, | ||||
|             { | ||||
|                 icon: 'fa-file-image-o', | ||||
|                 text: 'File to Base64', | ||||
|                 path: '/file-to-base64', | ||||
|                 component: FileToBase64 | ||||
|             }, | ||||
|         ], | ||||
|     }, | ||||
|     { | ||||
|         path: '/url-encoder', | ||||
|         component: UrlEncoder | ||||
|     }, | ||||
|     { | ||||
|         path: '/file-to-base64', | ||||
|         component: FileToBase64 | ||||
|     }, | ||||
|     { | ||||
|         path: '/cypher', | ||||
|         component: TextCypher | ||||
|         title: 'Miscellaneous', | ||||
|         child: [ | ||||
|             { | ||||
|                 icon: 'fa-file-text', | ||||
|                 text: 'Text stats', | ||||
|                 path: '/text-stats', | ||||
|                 component: TextStats | ||||
|             }, | ||||
|         ], | ||||
|     } | ||||
| ] | ||||
| ]; | ||||
| 
 | ||||
| const toolsComponentsFlat = toolsComponents.reduce((acc, section) => [...acc, ...section.child], []) | ||||
| 
 | ||||
| const routes = [ | ||||
|     ...toolsRoutes, | ||||
|     ...toolsComponentsFlat, | ||||
|     { | ||||
|         path: '/', | ||||
|         component: Home | ||||
| @ -59,5 +102,6 @@ const router = new VueRouter({ | ||||
| export default router; | ||||
| export { | ||||
|     routes, | ||||
|     toolsRoutes | ||||
|     toolsComponents, | ||||
|     toolsComponentsFlat | ||||
| }; | ||||
|  | ||||
| @ -2,7 +2,6 @@ | ||||
|   <v-card> | ||||
|     <v-card-text> | ||||
|       Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad at cumque dolore dolores, dolorum eius error est eum eveniet hic illum ipsum labore minus odio quibusdam repudiandae sed ullam veritatis! | ||||
| 
 | ||||
|     </v-card-text> | ||||
|   </v-card> | ||||
| </template> | ||||
|  | ||||
							
								
								
									
										72
									
								
								src/routes/tools/TextStats.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/routes/tools/TextStats.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| <template> | ||||
|     <v-card class="single-card"> | ||||
|         <v-card-title>Text stats</v-card-title> | ||||
|         <v-card-text> | ||||
|             <v-textarea | ||||
|                     outlined | ||||
|                     v-model="inputText" | ||||
|                     label="Input text" | ||||
|             /> | ||||
| 
 | ||||
|             <table> | ||||
|                 <tr> | ||||
|                     <td><strong>Character count:</strong></td> | ||||
|                     <td>{{ inputText.length }}</td> | ||||
|                 </tr> | ||||
|                 <tr> | ||||
|                     <td><strong>Character count (without spaces):</strong></td> | ||||
|                     <td>{{ inputText.split(' ').join('').length }}</td> | ||||
|                 </tr> | ||||
|                 <tr> | ||||
|                     <td><strong>Word count:</strong></td> | ||||
|                     <td>{{ inputText.length > 0 ? inputText.trim().split(/\s+/).length : 0 }}</td> | ||||
|                 </tr> | ||||
|                 <tr> | ||||
|                     <td><strong>Line count:</strong></td> | ||||
|                     <td>{{ inputText.length > 0 ? inputText.split(/\r\n|\r|\n/).length : 0 }}</td> | ||||
|                 </tr> | ||||
|                 <tr> | ||||
|                     <td><strong>Byte size:</strong></td> | ||||
|                     <td>{{ formatBytes(bytesSize) }} <span v-if="bytesSize >= 1024">({{bytesSize}} Bytes)</span></td> | ||||
|                 </tr> | ||||
|             </table> | ||||
|         </v-card-text> | ||||
|     </v-card> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
|     import {formatBytes} from "../../utils/helpers"; | ||||
| 
 | ||||
|     export default { | ||||
|         name: "TextStats", | ||||
|         data() { | ||||
|             return { | ||||
|                 Blob: Blob, | ||||
|                 formatBytes, | ||||
|                 inputText: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.', | ||||
|             } | ||||
|         }, | ||||
|         computed: { | ||||
|             bytesSize() { | ||||
|                 return new Blob([this.inputText]).size | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
| 
 | ||||
| <style scoped lang="less"> | ||||
|     table { | ||||
|         width: 100%; | ||||
| 
 | ||||
|         tr { | ||||
|             td { | ||||
|                 width: 50%; | ||||
|                 padding: 5px; | ||||
| 
 | ||||
|                 &:first-child { | ||||
|                     text-align: right; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
| @ -12,7 +12,20 @@ const fileIsImage = (file) => { | ||||
|     return file.type.split('/')[0] === 'image'; | ||||
| } | ||||
| 
 | ||||
| const formatBytes = (bytes, decimals = 2) => { | ||||
|     if (bytes === 0) return '0 Bytes'; | ||||
| 
 | ||||
|     const k = 1024; | ||||
|     const dm = decimals < 0 ? 0 : decimals; | ||||
|     const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; | ||||
| 
 | ||||
|     const i = Math.floor(Math.log(bytes) / Math.log(k)); | ||||
| 
 | ||||
|     return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; | ||||
| } | ||||
| 
 | ||||
| export { | ||||
|     copyToClipboard, | ||||
|     fileIsImage | ||||
|     fileIsImage, | ||||
|     formatBytes | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user