feat: TextStats + better searchbar

Signed-off-by: Corentin Thomasset <corentin.thomasset74@gmail.com>
This commit is contained in:
Corentin Thomasset 2020-06-01 18:27:49 +02:00
parent abc7fc6259
commit e5969a534f
6 changed files with 177 additions and 54 deletions

View file

@ -9,7 +9,7 @@
<div v-for="section in items" :key="section.title"> <div v-for="section in items" :key="section.title">
<v-subheader class="mt-4 pl-4">{{section.title}}</v-subheader> <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-list-item-action>
<v-icon>{{ item.icon }}</v-icon> <v-icon>{{ item.icon }}</v-icon>
</v-list-item-action> </v-list-item-action>
@ -82,6 +82,7 @@
<script> <script>
import SearchBar from "./components/SearchBar"; import SearchBar from "./components/SearchBar";
import {toolsComponents} from "./router";
export default { export default {
props: { props: {
@ -91,29 +92,7 @@
data: () => ({ data: () => ({
appVersion: 'v' + process.env.APPLICATION_VERSION, appVersion: 'v' + process.env.APPLICATION_VERSION,
drawer: null, drawer: null,
items: [ items: toolsComponents
{
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'},
],
}
],
}), }),
created() { created() {
this.$vuetify.theme.dark = true this.$vuetify.theme.dark = true

View file

@ -6,10 +6,13 @@
color="white" color="white"
hide-details hide-details
:items="items" :items="items"
item-text="component.name" item-text="text"
item-value="path" item-value="path"
solo-inverted solo-inverted
@change="choose" @change="choose"
:filter="filter"
clearable
cache-items
> >
<template v-slot:no-data> <template v-slot:no-data>
<v-list-item> <v-list-item>
@ -22,23 +25,36 @@
</template> </template>
<script> <script>
import {toolsComponentsFlat} from '../router'
import {toolsRoutes} from '../router' console.log(toolsComponentsFlat);
export default { export default {
name: "SearchBar", name: "SearchBar",
data() { data() {
const vm = this; const vm = this;
return { return {
items:toolsRoutes, items: toolsComponentsFlat,
choose(path) { choose(path) {
vm.$router.push(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> </script>
<style scoped> <style scoped lang="less">
::v-deep .v-list-item__mask{
color: inherit !important;
background: inherit !important;
}
</style> </style>

View file

@ -7,38 +7,81 @@ import DateConverter from "./routes/tools/DateConverter";
import UrlEncoder from "./routes/tools/UrlEncoder"; import UrlEncoder from "./routes/tools/UrlEncoder";
import FileToBase64 from "./routes/tools/FileToBase64"; import FileToBase64 from "./routes/tools/FileToBase64";
import TextCypher from "./routes/tools/TextCypher"; import TextCypher from "./routes/tools/TextCypher";
import TextStats from "./routes/tools/TextStats";
Vue.use(VueRouter) Vue.use(VueRouter)
const toolsRoutes = [
const toolsComponents = [
{ {
title: 'Crypto',
child: [
{
icon: 'fa-key',
text: 'Token generator',
path: '/token-generator', path: '/token-generator',
component: TokenGenerator component: TokenGenerator,
keywords: ['md5']
}, },
{ {
icon: 'fa-font',
text: 'Hash text',
path: '/hash', path: '/hash',
component: Hash component: Hash
}, },
{ {
icon: 'fa-lock',
text: 'Cypher/uncypher text',
path: '/cypher',
component: TextCypher
},
],
},
{
title: 'Converter',
child: [
{
icon: 'fa-calendar',
text: 'Date/Time converter',
path: '/date-converter', path: '/date-converter',
component: DateConverter component: DateConverter
}, },
],
},
{ {
title: 'Web',
child: [
{
icon: 'fa-link',
text: 'URL encode/decode',
path: '/url-encoder', path: '/url-encoder',
component: UrlEncoder component: UrlEncoder
}, },
{ {
icon: 'fa-file-image-o',
text: 'File to Base64',
path: '/file-to-base64', path: '/file-to-base64',
component: FileToBase64 component: FileToBase64
}, },
],
},
{ {
path: '/cypher', title: 'Miscellaneous',
component: TextCypher child: [
{
icon: 'fa-file-text',
text: 'Text stats',
path: '/text-stats',
component: TextStats
},
],
} }
] ];
const toolsComponentsFlat = toolsComponents.reduce((acc, section) => [...acc, ...section.child], [])
const routes = [ const routes = [
...toolsRoutes, ...toolsComponentsFlat,
{ {
path: '/', path: '/',
component: Home component: Home
@ -59,5 +102,6 @@ const router = new VueRouter({
export default router; export default router;
export { export {
routes, routes,
toolsRoutes toolsComponents,
toolsComponentsFlat
}; };

View file

@ -2,7 +2,6 @@
<v-card> <v-card>
<v-card-text> <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! 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-text>
</v-card> </v-card>
</template> </template>

View 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>

View file

@ -12,7 +12,20 @@ const fileIsImage = (file) => {
return file.type.split('/')[0] === 'image'; 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 { export {
copyToClipboard, copyToClipboard,
fileIsImage fileIsImage,
formatBytes
} }