feat: added components

This commit is contained in:
Corentin Thomasset 2021-03-14 20:11:39 +01:00
parent 6e0c369398
commit 5fa81533d9
No known key found for this signature in database
GPG key ID: DBD997E935996158
30 changed files with 2405 additions and 1853 deletions

68
CHANGELOG.md Normal file
View file

@ -0,0 +1,68 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Next
### Changed
- Switched to [Nuxt.js](//nuxtjs.org)
- Server-Side rendered static app
- Better SEO
- Switched to Typescript using class components decorators from [nuxt-property-decorator](https://github.com/nuxt-community/nuxt-property-decorator)
- UI and theme reworked
- URL path changed
- `/hash` -> [`/hash-text`](https://it-tools.tech/hash-text)
- `/cypher` -> [`/cypher-uncyfer-text`](https://it-tools.tech/cypher-uncyfer-text)
### Added
- Added [/how-to-report-bug-or-request](/how-to-report-bug-or-request) route to explain how to report bug and request features
## 1.7.0
- [feat] [Crontab friendly generator](https://it-tools.tech/crontab-generator)
## 1.6.0
- [feat] [BIP39 generator](https://it-tools.tech/bip39-generator)
- [feat] [Base 64 converter](https://it-tools.tech/base64-string-converter)
## 1.5.2
- [feat] [humans.txt](https://it-tools.tech/humans.txt)
- [feat] pwa auto update on new changes
## 1.5.1
- [feat] switched back to history mode (no more '#' in url)
## 1.5.0
- [feat] added [qr-code generator](https://it-tools.tech/qrcode-generator)
## 1.4.0
- [ui] condensed + colored sidenav
- [feat] added [git memo](https://it-tools.tech/git-memo)
- [refactor] changed app title
## 1.3.0
- [fix] [GithubContributors] ordered contributors by contribution count
- [refactor] used vue-typecasting for number inputs
- [feat] lazy loading tools routes
- [feat] added [markdown editor](https://it-tools.tech/markdown-editor)
- [feat] added [lorem ipsum generator](https://it-tools.tech/lorem-ipsum-generator)
## 1.2.1
- [fix] [UuidGenerator] added quantity validation rules
- [refactor] better isInt checker
## 1.2.0
- [feat] [UuidGenerator] can generate multiple uuids
## 1.1.0
- [feat] 404 route + page
- [feat] changelog in the About page
- [feat] contributors list in the About page
- [fix] [ColorConverter] color picker now updates fields
## 1.0.1
- [chore] added changelog
- [fix] [BaseConverter] prevented non-integer bases
- [fix] remove history move (incompatible with vercel.com)
## 1.0.0
- First release

View file

@ -3,3 +3,4 @@
// The variables you want to modify // The variables you want to modify
// $font-size-root: 20px; // $font-size-root: 20px;
$test: linear-gradient(90deg, rgba(37, 99, 108, 1) 0%, rgba(59, 149, 111, 1) 60%, rgba(71, 177, 113, 1) 100%)

View file

@ -2,14 +2,15 @@
<v-autocomplete <v-autocomplete
label="Search..." label="Search..."
single-line single-line
append-icon="fa-search" append-icon="mdi-magnify"
color="white" color="white"
hide-details hide-details
:items="items" :items="toolRoutesFlat"
item-text="text" :item-text="item => item.config.title"
item-value="path" item-value="path"
solo-inverted solo-inverted
:filter="filter" dense
:filter="filterItems"
clearable clearable
cache-items cache-items
@change="choose" @change="choose"
@ -24,23 +25,39 @@
</v-autocomplete> </v-autocomplete>
</template> </template>
<script> <script lang="ts">
import {Component, Vue} from 'nuxt-property-decorator' import {Component, mixins} from 'nuxt-property-decorator'
import {ToolRoutes} from '~/mixins/tool-routes' import {ToolRoutesMixin} from '@/mixins/tool-routes.mixin'
import {ToolRouteConfig} from '~/types/ToolConfig'
@Component({ @Component
mixins: [ToolRoutes] export default class SearchBar extends mixins(ToolRoutesMixin) {
}) choose(path:string) {
export default class SearchBar extends Vue { this.$router.push({path})
title = 'IT - Tools' }
drawer = false
items = [] filterItems(item:ToolRouteConfig, queryText:string, itemText:string) {
const query = queryText.trim().toLowerCase()
const nameContainsText = itemText.toLowerCase().includes(query)
const keywordContainsText = item?.config?.keywords.some((keyword:string) => keyword.toLowerCase().includes(query)) ?? false
return nameContainsText || keywordContainsText
}
} }
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
::v-deep .v-list-item__mask{ ::v-deep {
color: inherit !important; .v-input__slot{
background: inherit !important; background: var(--v-primary-base) !important;
background: linear-gradient(90deg, rgba(37,99,108,1) 0%, rgba(59,149,111,1) 60%, rgba(71,177,113,1) 100%) !important;
input {
color: #ffffff !important;
}
}
}
.v-list{
background: var(--v-foreground-base) !important;
} }
</style> </style>

View file

@ -1,7 +1,7 @@
<template> <template>
<div class="tool-wrapper"> <div class="tool-wrapper">
<v-row no-gutters justify="center" align="center"> <v-row no-gutters justify="center" align="center">
<v-col cols="12" lg="6"> <v-col cols="12" xl="6" lg="8" md="10">
<div class="tool-wrapper-info"> <div class="tool-wrapper-info">
<h1>{{ config.title }}</h1> <h1>{{ config.title }}</h1>
<div class="spacer" /> <div class="spacer" />
@ -50,9 +50,10 @@ export default class ToolWrapper extends Vue {
} }
.spacer{ .spacer{
width: 130px; width: 200px;
height: 1px; height: 2px;
background-color: var(--v-primary-base); background: var(--v-primary-base);
background: linear-gradient(90deg, rgba(71, 177, 113, 1) 0%, rgba(59, 149, 111, 1) 60%, rgba(37, 99, 108, 1) 200%);
margin-bottom: 10px; margin-bottom: 10px;
} }

View file

@ -17,6 +17,8 @@
</div> </div>
</div> </div>
<SearchBar class="hidden-sm-and-up" />
<v-list> <v-list>
<div v-for="(items, section) in toolRoutesSections" :key="section"> <div v-for="(items, section) in toolRoutesSections" :key="section">
<v-subheader class="mt-4 pl-4"> <v-subheader class="mt-4 pl-4">
@ -26,13 +28,15 @@
<v-list-item <v-list-item
v-for="(item, i) in items" v-for="(item, i) in items"
:key="i" :key="i"
:to="items.path" :to="item.path"
router router
exact exact
dense dense
> >
<v-list-item-action> <v-list-item-action>
<v-icon>{{ item.config.icon }}</v-icon> <v-icon color="primary">
{{ item.config.icon }}
</v-icon>
</v-list-item-action> </v-list-item-action>
<v-list-item-content> <v-list-item-content>
<v-list-item-title v-text="item.config.title" /> <v-list-item-title v-text="item.config.title" />
@ -48,8 +52,24 @@
height="60px" height="60px"
> >
<v-app-bar-nav-icon @click.stop="drawer = !drawer" /> <v-app-bar-nav-icon @click.stop="drawer = !drawer" />
<v-toolbar-title v-if="!drawer" v-text="title" /> <v-toolbar-title>
<NuxtLink to="/" class="title">
{{ title }}
</NuxtLink>
</v-toolbar-title>
<v-spacer /> <v-spacer />
<SearchBar class="hidden-sm-and-down" />
<v-spacer />
<NuxtLink to="/how-to-report-bug-or-request">
Bug / Request
</NuxtLink>
<NuxtLink to="/about">
About
</NuxtLink>
<a href="https://github.com/CorentinTh/it-tools" target="_blank" class="github-link">
<v-icon>mdi-github</v-icon>
</a>
</v-app-bar> </v-app-bar>
<v-main> <v-main>
@ -57,25 +77,27 @@
<nuxt /> <nuxt />
</v-container> </v-container>
</v-main> </v-main>
<!-- <v-footer app>--> <!--<v-footer app>-->
<!-- <span>&copy; {{ new Date().getFullYear() }}</span>--> <!-- <span>&copy; {{ new Date().getFullYear() }}</span>-->
<!-- </v-footer>--> <!--</v-footer>-->
</v-app> </v-app>
</template> </template>
<script lang="ts"> <script lang="ts">
import {Component, mixins} from 'nuxt-property-decorator' import {Component, mixins} from 'nuxt-property-decorator'
import {ToolRoutes} from '~/mixins/tool-routes' import {ToolRoutesMixin} from '~/mixins/tool-routes.mixin'
import LogoOutlined from '~/assets/logo-outlined.svg?inline' import LogoOutlined from '~/assets/logo-outlined.svg?inline'
import HeroGradient from '~/assets/small-hero-gradient.svg?inline' import HeroGradient from '~/assets/small-hero-gradient.svg?inline'
import SearchBar from '~/components/SearchBar.vue'
@Component({ @Component({
components: { components: {
LogoOutlined, LogoOutlined,
HeroGradient HeroGradient,
SearchBar
} }
}) })
export default class DefaultLayout extends mixins(ToolRoutes) { export default class DefaultLayout extends mixins(ToolRoutesMixin) {
title = 'IT - Tools' title = 'IT - Tools'
drawer = false drawer = false
items = [] items = []
@ -84,6 +106,40 @@ export default class DefaultLayout extends mixins(ToolRoutes) {
<style lang="less"> <style lang="less">
.v-toolbar__content {
a {
color: #ffffff;
text-decoration: none;
transition: all ease 0.2s;
margin: 0 10px;
opacity: 0.5;
font-size: 15px;
&.title{
opacity: 1;
}
&:hover {
opacity: 1;
color: var(--v-primary-base);
}
}
.github-link {
border-bottom: none;
margin-left: 10px;
transition: all ease 0.2s;
.v-icon {
font-size: 37px !important;
color: var(--v-primary-base);
transition: all ease 0.2s;
transition: all ease 0.2s;
}
}
}
.small-hero { .small-hero {
position: relative; position: relative;
@ -96,18 +152,26 @@ export default class DefaultLayout extends mixins(ToolRoutes) {
width: 100%; width: 100%;
.small-hero-content-logo { .small-hero-content-logo {
width: 30%; width: 25%;
margin: 0 auto; margin: 0 auto;
} }
.small-hero-content-title { .small-hero-content-title {
font-size: 30px; margin-top: 10px;
font-size: 25px;
font-weight: 600; font-weight: 600;
font-family: Ubuntu, Roboto, sans-serif; font-family: Ubuntu, Roboto, sans-serif;
} }
} }
} }
.v-navigation-drawer__content{
.v-list-item--active{
color: var(--v-anchor-base);
border-left: 3px solid var(--v-primary-base);
}
}
.v-application { .v-application {
background-color: var(--v-background-base, #121212) !important; background-color: var(--v-background-base, #121212) !important;
} }

View file

@ -11,7 +11,7 @@ const copyToClipboard = (text: string) => {
} }
@Component @Component
export class Copyable extends Vue { export class CopyableMixin extends Vue {
copy(text: string, toastText = 'Copied to clipboard !') { copy(text: string, toastText = 'Copied to clipboard !') {
copyToClipboard(text) copyToClipboard(text)
console.log(toastText) console.log(toastText)

View file

@ -1,33 +1,36 @@
import {Component, Vue} from 'nuxt-property-decorator' import {Component, Vue} from 'nuxt-property-decorator'
import {RouteConfig} from '@nuxt/types/config/router' import {ToolRouteConfig} from '~/types/ToolConfig'
import {ToolConfig} from '~/types/ToolConfig'
import {capitalise} from '~/utils/string' import {capitalise} from '~/utils/string'
export type ToolRouteConfig = RouteConfig & {config: ToolConfig}
@Component @Component
export class ToolRoutes extends Vue { export class ToolRoutesMixin extends Vue {
toolRoutesFlat : ToolRouteConfig[] = [] toolRoutesFlat : ToolRouteConfig[] = []
toolRoutesSections : {[key: string]: ToolRouteConfig[]} = {} toolRoutesSections : {[key: string]: ToolRouteConfig[]} = {}
async mounted() { async created() {
const routes = this.$router.options.routes?.filter(r => r.meta?.isTool) || [] const routes = this.$router.options.routes?.filter(r => r.meta?.isTool) || []
const flat: ToolRouteConfig[] = []
const sections: { [key: string]: ToolRouteConfig[] } = {}
for (const route of routes) { for (const route of routes) {
if ('component' in route) { if ('component' in route) {
// @ts-ignore // @ts-ignore
const component = await route.component() const component = await route.component()
const routeConfig = {...route, config: component.options.methods.config()} as ToolRouteConfig const routeConfig = {...route, config: component.options.methods.config()} as ToolRouteConfig
this.toolRoutesFlat.push(routeConfig) flat.push(routeConfig)
const sectionKey = capitalise(route.meta.section).replace(/_/g, ' ') const sectionKey = capitalise(route.meta.section).replace(/_/g, ' ')
if (!(sectionKey in this.toolRoutesSections)) { if (!(sectionKey in sections)) {
this.toolRoutesSections[sectionKey] = [] sections[sectionKey] = []
} }
this.toolRoutesSections[sectionKey].push(routeConfig) sections[sectionKey].push(routeConfig)
} }
} }
this.toolRoutesSections = sections
this.toolRoutesFlat = flat
} }
} }

3514
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -13,11 +13,13 @@
}, },
"dependencies": { "dependencies": {
"@nuxt/typescript-runtime": "^2.0.1", "@nuxt/typescript-runtime": "^2.0.1",
"@nuxtjs/axios": "^5.12.2", "@nuxtjs/axios": "^5.13.1",
"@nuxtjs/pwa": "^3.0.2", "@nuxtjs/pwa": "^3.0.2",
"@nuxtjs/toast": "^3.3.1", "@nuxtjs/toast": "^3.3.1",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"crypto-js": "^4.0.0",
"nuxt": "^2.14.12", "nuxt": "^2.14.12",
"vuetify": "^2.4.5",
"vuetify-toast-snackbar": "^0.6.1" "vuetify-toast-snackbar": "^0.6.1"
}, },
"devDependencies": { "devDependencies": {
@ -28,11 +30,12 @@
"@nuxtjs/eslint-module": "^2.0.0", "@nuxtjs/eslint-module": "^2.0.0",
"@nuxtjs/svg": "^0.1.12", "@nuxtjs/svg": "^0.1.12",
"@nuxtjs/vuetify": "^1.11.2", "@nuxtjs/vuetify": "^1.11.2",
"@types/crypto-js": "^4.0.1",
"@vue/test-utils": "^1.1.0", "@vue/test-utils": "^1.1.0",
"babel-core": "7.0.0-bridge.0", "babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-jest": "^26.5.0", "babel-jest": "^26.5.0",
"eslint": "^7.10.0", "eslint": "^7.20.0",
"eslint-config-prettier": "^6.12.0", "eslint-config-prettier": "^6.12.0",
"eslint-plugin-nuxt": "^1.0.0", "eslint-plugin-nuxt": "^1.0.0",
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-prettier": "^3.1.4",
@ -40,7 +43,7 @@
"less": "^4.0.0", "less": "^4.0.0",
"less-loader": "^7.1.0", "less-loader": "^7.1.0",
"nuxt-property-decorator": "^2.9.1", "nuxt-property-decorator": "^2.9.1",
"ts-jest": "^26.4.1", "ts-jest": "^26.5.1",
"vue-jest": "^3.0.4" "vue-jest": "^3.0.4"
} }
} }

20
pages/about.vue Normal file
View file

@ -0,0 +1,20 @@
<template>
<v-row justify="center" align="center">
<v-col cols="12" sm="12" md="8">
<h1>Yolo</h1>
</v-col>
</v-row>
</template>
<script>
import {Component, Vue} from 'nuxt-property-decorator'
@Component
export default class About extends Vue {
}
</script>
<style lang="less">
</style>

View file

@ -0,0 +1,20 @@
<template>
<v-row justify="center" align="center">
<v-col cols="12" sm="12" md="8">
<h1>How-to-report-bug-or-request</h1>
</v-col>
</v-row>
</template>
<script>
import {Component, Vue} from 'nuxt-property-decorator'
@Component
export default class HowToReportBugOrRequest extends Vue {
}
</script>
<style lang="less">
</style>

View file

@ -2,17 +2,57 @@
<v-row justify="center" align="center"> <v-row justify="center" align="center">
<v-col cols="12" sm="12" md="8"> <v-col cols="12" sm="12" md="8">
<h1>Yolo</h1> <h1>Yolo</h1>
<v-card v-for="(items, section) in toolRoutesSections" :key="section">
<v-card-title>{{ section }}</v-card-title>
<v-card-text>
<v-list>
<v-list-item
v-for="(item, i) in items"
:key="i"
:to="item.path"
router
exact
>
<v-list-item-action>
<v-icon>{{ item.config.icon }}</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title v-text="item.config.title" />
</v-list-item-content>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-col> </v-col>
</v-row> </v-row>
</template> </template>
<script> <script>
import {Component, mixins} from 'nuxt-property-decorator'
import {ToolRoutesMixin} from '@/mixins/tool-routes.mixin'
export default { @Component
components: {} export default class Index extends mixins(ToolRoutesMixin) {
} }
</script> </script>
<style lang="less"> <style scopedlang="less">
.v-list{
background: transparent !important;
}
.v-card__title{
background: var(--v-primary-base) !important;
background: linear-gradient(90deg, rgba(37,99,108,1) 0%, rgba(59,149,111,1) 60%, rgba(71,177,113,1) 100%) !important;
padding-left: 33px;
}
.v-list-item{
padding-left: 31px;
}
.v-card__text{
padding: 0;
}
</style> </style>

View file

@ -0,0 +1,106 @@
<template>
<ToolWrapper :config="config()">
<v-row justify="center" align="center">
<v-col cols="12" lg="8" md="12">
<v-textarea
v-model="key"
outlined
label="Encryption key"
rows="1"
@input="encrypt"
/>
</v-col>
<v-col cols="12" lg="4" md="12">
<v-select
v-model="algorithm"
:items="Object.keys(algorithms)"
label="Algorithm"
outlined
@change="encrypt"
/>
</v-col>
</v-row>
<v-textarea
v-model="decrypted"
outlined
label="Clear text"
@input="encrypt"
/>
<v-textarea
v-model="encrypted"
outlined
label="Cyphered text"
@input="decrypt"
/>
<div class="text-center">
<v-btn depressed @click="copy(encrypted)">
Copy result
</v-btn>
</div>
</ToolWrapper>
</template>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import {CopyableMixin} from '@/mixins/copyable.mixin'
import Tool from '@/components/Tool'
import {ToolConfig} from '@/types/ToolConfig'
import CryptoJS from 'crypto-js'
const algos = {
AES: CryptoJS.AES,
TripleDES: CryptoJS.TripleDES,
Rabbit: CryptoJS.Rabbit,
RabbitLegacy: CryptoJS.RabbitLegacy,
RC4: CryptoJS.RC4
}
@Component({
mixins: [CopyableMixin]
})
export default class CypherUncyferText extends Tool {
algorithm: keyof typeof algos = 'AES'
algorithms: typeof algos = algos
key = 'sup3r s3cr3t k3y'
decrypted = 'Lorem ipsum dolor sit amet.'
encrypted = ''
config(): ToolConfig {
return {
title: 'Cypher / uncypher text',
description: 'Cypher and uncyfer text.',
icon: 'mdi-lock-open',
keywords: ['cypher', 'uncypher', 'text', ...Object.keys(algos).map(s => s.toLowerCase())]
}
}
mounted() {
this.encrypt()
}
encrypt() {
try {
this.encrypted = this.algorithms[this.algorithm]
.encrypt(this.decrypted.trim(), this.key)
.toString()
} catch (ignored) {
// ignored
}
}
decrypt() {
try {
this.decrypted = this.algorithms[this.algorithm]
.decrypt(this.encrypted.trim(), this.key)
.toString(CryptoJS.enc.Utf8)
} catch (ignored) {
// ignored
}
}
}
</script>
<style lang="less">
</style>

View file

@ -0,0 +1,78 @@
<template>
<ToolWrapper :config="config()">
<v-textarea
v-model="inputText"
outlined
label="Text to hash"
/>
<v-select
v-model="algorithm"
:items="Object.keys(algorithms)"
label="Algorithm"
outlined
/>
<v-textarea
v-model="hashed"
outlined
readonly
label="Hashed text"
/>
<div class="text-center">
<v-btn depressed @click="copy(hashed)">
Copy hash
</v-btn>
</div>
</ToolWrapper>
</template>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import CryptoJS from 'crypto-js'
import {CopyableMixin} from '~/mixins/copyable.mixin'
import Tool from '~/components/Tool.vue'
import {ToolConfig} from '~/types/ToolConfig'
const algos = {
MD5: CryptoJS.MD5,
SHA1: CryptoJS.SHA1,
SHA256: CryptoJS.SHA256,
SHA224: CryptoJS.SHA224,
SHA512: CryptoJS.SHA512,
SHA384: CryptoJS.SHA384,
SHA3: CryptoJS.SHA3,
RIPEMD160: CryptoJS.RIPEMD160
}
@Component({
mixins: [CopyableMixin]
})
export default class HashText extends Tool {
config(): ToolConfig {
return {
title: 'Hash text',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-script-text-play',
keywords: ['hash', 'text', ...Object.keys(algos).map(s => s.toLowerCase())]
}
}
inputText = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.'
algorithm: keyof typeof algos = 'SHA256'
algorithms: typeof algos = algos
get hashed() {
if (this.algorithms[this.algorithm]) {
return this.algorithms[this.algorithm](this.inputText).toString()
} else {
this.$toast.error('Invalid algorithm.')
return ''
}
}
}
</script>
<style>
</style>

View file

@ -11,7 +11,7 @@
</v-col> </v-col>
</v-row> </v-row>
<v-slider v-model="length" :label="`Length (${length})`" min="1" max="256" /> <v-slider v-model="length" :label="`Length (${length})`" min="1" max="512" />
<v-textarea v-model="token" outlined /> <v-textarea v-model="token" outlined />
@ -30,16 +30,16 @@
import {Component} from 'nuxt-property-decorator' import {Component} from 'nuxt-property-decorator'
import Tool from '~/components/Tool.vue' import Tool from '~/components/Tool.vue'
import {ToolConfig} from '~/types/ToolConfig' import {ToolConfig} from '~/types/ToolConfig'
import {Copyable} from '~/mixins/copyable' import {CopyableMixin} from '~/mixins/copyable.mixin'
import {shuffle} from '~/utils/string'
const shuffle = (s: string) => s.split('').sort(() => 0.5 - Math.random()).join('')
const lowercase = 'abcdefghijklmopqrstuvwxyz' const lowercase = 'abcdefghijklmopqrstuvwxyz'
const uppercase = 'ABCDEFGHIJKLMOPQRSTUVWXYZ' const uppercase = 'ABCDEFGHIJKLMOPQRSTUVWXYZ'
const numbers = '0123456789' const numbers = '0123456789'
const specials = '.,;:!?./-"\'#{([-|\\@)]=}*+' const specials = '.,;:!?./-"\'#{([-|\\@)]=}*+'
@Component({ @Component({
mixins: [Copyable] mixins: [CopyableMixin]
}) })
export default class TokenGenerator extends Tool { export default class TokenGenerator extends Tool {
config(): ToolConfig { config(): ToolConfig {

View file

@ -0,0 +1,92 @@
<template>
<ToolWrapper :config="config()">
<v-text-field
v-model.number="quantity"
outlined
type="number"
label="Quantity"
dense
class="quantity"
:rules="rules.quantity"
/>
<v-textarea
v-model="token"
outlined
class="centered-input"
:rows="quantity <= 10 ? quantity : 10"
readonly
/>
<div class="text-center">
<v-btn depressed class="mr-4" @click="computeToken">
Refresh
</v-btn>
<v-btn depressed @click="copy(token, `UUID${quantity > 1 ? 's' : ''} copied !`)">
Copy UUID{{ quantity > 1 ? 's' : '' }}
</v-btn>
</div>
</ToolWrapper>
</template>
<script lang="ts">
import {Component, Ref, Watch} from 'nuxt-property-decorator'
import {CopyableMixin} from '@/mixins/copyable.mixin'
import {ToolConfig} from '@/types/ToolConfig'
import { VTextField } from 'vuetify/lib'
import Tool from '~/components/Tool.vue'
const generateUuid = () => '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, c => ((c as unknown as number) ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> (c as unknown as number) / 4).toString(16))
@Component({
mixins: [CopyableMixin]
})
export default class UuidGenerator extends Tool {
config(): ToolConfig {
return {
title: 'UUIDs generator',
description: 'A universally unique identifier (UUID) is a 128-bit number used to identify information in computer systems. ',
icon: 'mdi-fingerprint',
keywords: ['uuid', 'v4', 'random', 'id', 'alphanumeric', 'identity']
}
}
@Ref() readonly quantityEl! : typeof VTextField
token = ''
quantity = 1
rules = {
quantity: [
(v: any) => !!v || 'Quantity is required',
(v: any) => (v > 0 && v <= 50) || 'Quantity should be > 0 and <= 50',
(v: any) => Number.isInteger(v) || 'Quantity should be an integer'
]
}
mounted() {
this.computeToken()
}
@Watch('quantity')
computeToken() {
this.token = Array.from({length: this.quantity}, generateUuid).join('\n')
}
}
</script>
<style scoped lang="less">
.quantity {
width: 100px;
margin: auto;
text-align: center;
::v-deep input {
text-align: center;
}
}
::v-deep .centered-input textarea {
text-align: center;
margin-top: 13px !important;
font-family: Consolas, monospace;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
static/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

9
static/browserconfig.xml Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#2b5797</TileColor>
</tile>
</msapplication>
</browserconfig>

BIN
static/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
static/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

BIN
static/mstile-150x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -0,0 +1,83 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="726.000000pt" height="726.000000pt" viewBox="0 0 726.000000 726.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,726.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M3255 7239 c-112 -39 -191 -120 -230 -238 -14 -43 -17 -88 -17 -237
0 -144 -3 -186 -14 -195 -8 -6 -14 -9 -14 -6 0 3 -23 -1 -52 -9 -28 -9 -60
-17 -71 -19 -10 -3 -28 -7 -40 -11 -12 -5 -26 -8 -32 -9 -5 -1 -17 -4 -25 -7
-8 -3 -53 -19 -100 -34 -47 -15 -89 -31 -94 -36 -6 -4 -16 -8 -24 -8 -15 0
-68 -22 -114 -46 -16 -9 -28 -12 -28 -7 0 4 -4 4 -8 -2 -6 -9 -103 -58 -109
-56 -2 0 -46 -24 -99 -54 -53 -30 -100 -55 -105 -55 -5 0 -9 -3 -9 -7 0 -9
-57 -43 -71 -43 -5 0 -69 60 -142 133 -132 130 -163 152 -264 185 -76 24 -223
-4 -292 -57 -56 -42 -441 -432 -466 -471 -60 -96 -75 -246 -32 -325 6 -11 12
-25 13 -32 4 -23 60 -88 177 -205 64 -64 117 -120 117 -123 0 -4 -13 -25 -29
-48 -15 -23 -31 -52 -35 -64 -4 -13 -11 -23 -15 -23 -5 0 -14 -15 -22 -32 -7
-18 -16 -35 -19 -38 -5 -4 -82 -157 -85 -170 -1 -3 -9 -23 -19 -45 -41 -92
-103 -255 -111 -290 -1 -5 -5 -17 -8 -25 -9 -20 -47 -164 -59 -222 -6 -27 -13
-48 -17 -49 -3 0 -91 -2 -196 -3 -105 -1 -208 -8 -231 -14 -120 -35 -230 -160
-251 -287 -5 -27 -8 -185 -7 -350 1 -282 3 -303 23 -357 41 -106 113 -177 222
-220 48 -18 77 -21 249 -22 l195 -1 13 -50 c7 -27 15 -58 17 -68 2 -10 7 -27
10 -37 3 -10 7 -27 9 -37 15 -75 116 -345 128 -341 5 2 186 180 402 396 l393
393 -8 44 c-24 126 -32 424 -15 550 5 33 9 71 10 85 10 99 86 364 138 480 70
156 158 310 222 392 12 14 32 41 46 59 85 113 253 277 380 372 116 87 304 191
440 244 120 47 141 55 155 58 6 2 35 10 65 19 30 8 73 18 95 22 22 3 44 8 48
11 4 2 22 7 40 9 18 3 43 7 57 9 112 18 365 24 485 11 299 -32 547 -112 810
-260 39 -22 72 -43 75 -46 3 -3 26 -19 52 -35 27 -17 48 -33 48 -37 0 -5 5 -8
11 -8 11 0 84 -59 168 -135 41 -37 58 -54 141 -145 50 -54 142 -178 194 -258
90 -142 216 -429 241 -552 2 -8 8 -33 13 -55 6 -22 13 -53 16 -70 3 -16 7 -41
10 -55 7 -36 17 -109 21 -155 6 -61 6 -315 0 -360 -25 -201 -29 -221 -80 -405
-31 -114 -117 -310 -183 -420 -12 -19 -29 -48 -38 -65 -9 -16 -20 -32 -23 -35
-3 -3 -19 -24 -34 -48 -32 -50 -172 -217 -215 -259 -95 -90 -183 -165 -242
-206 -154 -107 -272 -173 -410 -230 -79 -33 -223 -82 -266 -92 -228 -50 -308
-60 -499 -59 -170 0 -297 10 -355 29 -14 4 -111 -87 -414 -390 -218 -218 -396
-400 -396 -405 0 -4 24 -16 53 -26 28 -9 57 -20 62 -24 6 -4 57 -22 115 -39
58 -18 116 -37 130 -41 14 -5 50 -14 80 -20 106 -23 97 -1 98 -223 0 -122 5
-203 12 -216 6 -12 8 -21 5 -21 -3 0 6 -22 20 -49 27 -53 103 -141 123 -141 7
0 12 -4 12 -9 0 -5 17 -15 38 -22 20 -6 43 -17 51 -23 10 -8 117 -11 350 -11
307 0 341 2 394 20 31 10 57 23 57 27 0 4 6 8 14 8 20 0 112 96 131 137 36 75
42 119 42 307 1 103 2 189 5 191 2 2 15 6 28 9 140 29 398 113 533 174 37 17
67 28 67 24 0 -4 4 -2 8 3 4 6 43 28 87 49 44 22 82 42 85 45 3 4 17 12 32 18
15 7 46 25 70 40 98 64 83 69 228 -76 138 -139 184 -172 270 -193 92 -23 172
-12 265 36 40 21 440 412 487 476 77 107 85 261 19 386 -12 23 -83 103 -157
178 l-136 137 19 26 c22 32 48 75 76 128 12 22 24 42 28 45 6 6 102 202 103
211 1 3 12 30 25 60 13 30 27 63 32 74 17 42 71 203 85 255 19 69 45 170 48
190 2 13 32 15 187 16 201 2 230 5 296 35 46 21 70 38 110 79 24 24 75 104 80
125 1 5 7 31 13 56 12 49 9 646 -3 688 -33 117 -150 231 -262 257 -16 3 -115
7 -219 8 -211 3 -199 -2 -216 83 -5 26 -19 80 -30 118 -12 39 -22 77 -25 85
-2 8 -4 16 -5 18 -2 1 -3 5 -5 10 -1 4 -5 14 -8 22 -4 8 -14 38 -23 65 -9 28
-21 61 -27 75 -5 14 -11 30 -12 36 -7 29 -149 311 -202 400 l-60 102 136 138
c91 92 144 155 159 187 68 144 44 300 -63 415 -114 123 -402 403 -435 424 -95
58 -234 73 -325 34 -22 -9 -42 -17 -45 -18 -13 -1 -86 -67 -191 -172 -64 -64
-124 -116 -132 -116 -9 0 -25 10 -37 22 -11 12 -20 18 -20 12 0 -5 -4 -4 -8 2
-9 13 -185 111 -277 154 -73 34 -263 111 -280 114 -5 1 -14 4 -20 7 -5 4 -37
14 -70 24 -33 10 -87 26 -120 36 -33 10 -85 23 -115 29 l-55 12 0 207 -1 206
-29 60 c-29 60 -125 165 -151 165 -8 0 -14 4 -14 8 0 5 -26 16 -57 25 -48 13
-115 16 -373 16 -293 1 -319 -1 -375 -20z"/>
<path d="M3577 5134 c-1 -1 -44 -4 -94 -8 -51 -4 -98 -8 -105 -11 -7 -2 -24
-6 -38 -9 -106 -19 -283 -79 -375 -126 -65 -34 -177 -100 -185 -109 -3 -4 -23
-18 -45 -33 -22 -15 -56 -42 -76 -60 -20 -18 -47 -43 -60 -55 -74 -66 -182
-197 -237 -288 -56 -90 -130 -249 -152 -325 -13 -41 -27 -80 -31 -86 -5 -6 -7
-13 -4 -16 3 -2 0 -20 -5 -39 -6 -19 -13 -52 -16 -74 -3 -22 -8 -51 -10 -65
-3 -14 -6 -88 -8 -165 -3 -123 7 -245 28 -345 3 -14 8 -35 10 -48 3 -12 15
-54 28 -92 l22 -71 -1037 -1037 c-570 -570 -1053 -1059 -1072 -1086 -19 -27
-35 -55 -35 -63 0 -7 -3 -13 -8 -13 -8 0 -31 -48 -37 -79 -2 -12 -6 -25 -9
-29 -21 -34 -29 -211 -14 -291 9 -51 55 -180 67 -191 3 -3 12 -17 19 -31 17
-33 116 -137 162 -171 110 -80 245 -120 388 -114 110 5 176 22 277 74 65 33
151 116 1128 1092 l1058 1058 49 -19 c27 -10 59 -20 72 -23 13 -3 32 -7 43
-10 148 -34 189 -39 350 -39 150 -1 226 7 345 34 82 19 233 70 275 92 11 6 22
12 25 12 23 4 240 133 260 155 3 3 25 21 50 41 134 108 265 260 348 404 49 86
121 250 137 315 1 3 4 12 7 20 10 24 37 146 44 195 11 70 14 116 15 215 0 101
-8 237 -16 250 -2 3 -6 26 -10 50 -22 151 -160 233 -287 171 -28 -14 -133
-111 -303 -282 -143 -144 -282 -279 -309 -301 -131 -104 -308 -135 -469 -81
-39 14 -74 28 -77 31 -3 4 -17 13 -32 21 -39 20 -109 89 -144 142 -106 157
-115 340 -26 513 31 59 78 111 328 362 160 161 298 306 307 322 8 16 14 57 15
90 0 63 -24 113 -73 154 -34 29 -172 62 -280 68 -69 4 -175 6 -178 4z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

19
static/site.webmanifest Normal file
View file

@ -0,0 +1,19 @@
{
"name": "IT - Tools",
"short_name": "IT Tools",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View file

@ -28,7 +28,8 @@
"@types/node", "@types/node",
"@nuxtjs/toast", "@nuxtjs/toast",
"@nuxt/types", "@nuxt/types",
"~/types/custom.d.ts" "~/types/custom.d.ts",
"vuetify"
] ]
}, },
"exclude": [ "exclude": [

View file

@ -1,3 +1,5 @@
import {RouteConfig} from '@nuxt/types/config/router';
interface ToolConfig { interface ToolConfig {
title: string; title: string;
description: string; description: string;
@ -6,5 +8,6 @@ interface ToolConfig {
} }
type ToolConfigMethod = () => ToolConfig; type ToolConfigMethod = () => ToolConfig;
type ToolRouteConfig = RouteConfig & {config: ToolConfig}
export {ToolConfig, ToolConfigMethod} export {ToolConfig, ToolConfigMethod, ToolRouteConfig}

View file

@ -1,5 +1,5 @@
function capitalise(s: string) { const capitalise = (s: string) => s.charAt(0).toUpperCase() + s.slice(1)
return s.charAt(0).toUpperCase() + s.slice(1)
}
export {capitalise} const shuffle = (s: string) => s.split('').sort(() => 0.5 - Math.random()).join('')
export {capitalise, shuffle}