reset commits
This commit is contained in:
commit
cf5af3cc23
55 changed files with 6825 additions and 0 deletions
1
src/assets/css/variables.sass
Normal file
1
src/assets/css/variables.sass
Normal file
|
|
@ -0,0 +1 @@
|
|||
$background: #0f1a28
|
||||
3
src/assets/css/viewport.sass
Normal file
3
src/assets/css/viewport.sass
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
$mobile: 375px
|
||||
$tablet: 768px
|
||||
$desktop: 1440px
|
||||
BIN
src/assets/images/aio-tools.png
Normal file
BIN
src/assets/images/aio-tools.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
0
src/components/AddItem/AddItem.sass
Normal file
0
src/components/AddItem/AddItem.sass
Normal file
12
src/components/AddItem/AddItem.tsx
Normal file
12
src/components/AddItem/AddItem.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { AiTwotonePlusCircle } from 'solid-icons/ai'
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<>
|
||||
<section class="section">
|
||||
<AiTwotonePlusCircle />
|
||||
<h1>Add Item</h1>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
23
src/components/Alert/Alert.sass
Normal file
23
src/components/Alert/Alert.sass
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
@use '/src/assets/css/variables.sass' as vars
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
$textColor: rgba(126, 206, 241, 0.786)
|
||||
|
||||
.alert-wrapper
|
||||
position: fixed
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
z-index: 1000
|
||||
|
||||
.alert
|
||||
border-radius: 6px
|
||||
padding: 1rem 1.25rem
|
||||
background-color: color.adjust(vars.$background, $lightness: 3%)
|
||||
border: 1px solid color.adjust(vars.$background, $lightness: 20%)
|
||||
color: $textColor
|
||||
font-size: 1.25rem
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
font-size: 1rem
|
||||
13
src/components/Alert/Alert.tsx
Normal file
13
src/components/Alert/Alert.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import './Alert.sass'
|
||||
import { Alert } from '@kobalte/core/alert'
|
||||
import { Show } from 'solid-js'
|
||||
|
||||
export default (props: { for: string; trigger: boolean }) => {
|
||||
return (
|
||||
<div class="alert-wrapper">
|
||||
<Show when={props.trigger}>
|
||||
<Alert class="alert">{props.for + ' copied to clipboard'}</Alert>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
55
src/components/Button/Button.sass
Normal file
55
src/components/Button/Button.sass
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
$backgroundColor: #1b7278
|
||||
$backgroundColorHover: color.adjust($backgroundColor, $blackness: 11%)
|
||||
$coffeeColor: #1a726e
|
||||
$coffeeColorHover: color.adjust($coffeeColor, $lightness: 11%)
|
||||
$fontFamily: 'Inter', sans-serif
|
||||
|
||||
.button
|
||||
border: none
|
||||
border-radius: 50%
|
||||
border-radius: 16px
|
||||
padding: 0.5rem 1.5rem
|
||||
font-size: 1rem
|
||||
color: #ffffff
|
||||
background-color: $backgroundColor
|
||||
cursor: pointer
|
||||
font-family: $fontFamily
|
||||
transition: background-color 0.2s ease-out
|
||||
|
||||
&:hover
|
||||
background-color: $backgroundColorHover
|
||||
|
||||
&:active
|
||||
transform: scale(0.9)
|
||||
|
||||
.coffee-button
|
||||
display: flex
|
||||
align-items: center
|
||||
border: none
|
||||
border-radius: 16px
|
||||
padding: 0.5rem 2rem
|
||||
font-size: 1rem
|
||||
color: #ffffff
|
||||
background-color: $coffeeColor
|
||||
cursor: pointer
|
||||
font-family: $fontFamily
|
||||
transition: all 0.2s ease-out
|
||||
|
||||
&:hover
|
||||
background-color: $coffeeColorHover
|
||||
transform: scale(1.2)
|
||||
margin: 0 1rem 0 1rem
|
||||
|
||||
&:active
|
||||
transform: scale(0.9)
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
display: none
|
||||
|
||||
.coffee-icon
|
||||
margin: 0 0.5rem 0 0
|
||||
font-size: 1.5rem
|
||||
color: #1b1d18
|
||||
23
src/components/Button/Button.tsx
Normal file
23
src/components/Button/Button.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import './Button.sass'
|
||||
import { Show } from 'solid-js'
|
||||
import { BiSolidCoffee } from 'solid-icons/bi'
|
||||
import { SiBuymeacoffee } from 'solid-icons/si'
|
||||
|
||||
export default (props: { label: string; forCoffee?: boolean; onClick?: () => void }) => {
|
||||
return (
|
||||
<section>
|
||||
<Show when={props.forCoffee}>
|
||||
<button class="coffee-button" onClick={props.onClick}>
|
||||
<SiBuymeacoffee class="coffee-icon" />
|
||||
{props.label}
|
||||
</button>
|
||||
</Show>
|
||||
|
||||
<Show when={!props.forCoffee}>
|
||||
<button class="button" onClick={props.onClick}>
|
||||
{props.label}
|
||||
</button>
|
||||
</Show>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
46
src/components/Card/Card.sass
Normal file
46
src/components/Card/Card.sass
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
$borderColor: rgba(72, 100, 114, 0.463)
|
||||
$borderColorHover: rgba(88, 133, 135, 0.931)
|
||||
$backgroundColor: #1e1e1e
|
||||
$borderRadius: 8px
|
||||
$boxShadow: rgba(72, 100, 114, 0.463) 0px 4px 12px
|
||||
$textColor: #ffffff
|
||||
|
||||
.card
|
||||
height: 10rem
|
||||
// width: clamp(10rem, 25vw, 24rem)
|
||||
width: 24rem
|
||||
border: 2px solid $borderColor
|
||||
padding: 20px
|
||||
margin-bottom: 20px
|
||||
cursor: pointer
|
||||
color: $textColor
|
||||
border-radius: $borderRadius
|
||||
transition: border-color 0.3s ease-out
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
width: clamp(9rem, 16rem, 16rem)
|
||||
|
||||
@media screen and (max-width: view.$mobile)
|
||||
width: clamp(9rem, 12rem, 16rem)
|
||||
|
||||
&:hover
|
||||
border-color: $borderColorHover
|
||||
|
||||
&__icon
|
||||
opacity: 0.5
|
||||
transition: opacity 0.3s ease-out
|
||||
|
||||
&:hover .card__icon
|
||||
color: color.adjust($borderColorHover, $lightness: 20%)
|
||||
opacity: 1
|
||||
|
||||
&__title
|
||||
@media screen and (max-width: view.$mobile)
|
||||
font-size: 1rem
|
||||
&__description
|
||||
@media screen and (max-width: view.$mobile)
|
||||
font-size: 0.8rem
|
||||
opacity: 0.8
|
||||
19
src/components/Card/Card.tsx
Normal file
19
src/components/Card/Card.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { Show, type JSX } from 'solid-js'
|
||||
import './Card.sass'
|
||||
import { RiSystemLockPasswordLine } from 'solid-icons/ri'
|
||||
import { FiHash } from 'solid-icons/fi'
|
||||
|
||||
export default (props: { title: string; description: string }) => {
|
||||
return (
|
||||
<section class="card">
|
||||
<Show when={props.title === 'Password Generator'}>
|
||||
<RiSystemLockPasswordLine class="card__icon" size={32} opacity={0.5} />
|
||||
</Show>
|
||||
<Show when={props.title === 'Hash Generator'}>
|
||||
<FiHash class="card__icon" size={32} opacity={0.5} />
|
||||
</Show>
|
||||
<h3 class="card__title">{props.title}</h3>
|
||||
<p class="card__description">{props.description}</p>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
119
src/components/Hash-Generator/HashGeneratorComponent.sass
Normal file
119
src/components/Hash-Generator/HashGeneratorComponent.sass
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
@use '/src/assets/css/variables.sass' as vars
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
.section
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: flex-start
|
||||
padding: 2rem
|
||||
border: 1px solid rgba(255, 255, 255, 0.3)
|
||||
border-radius: 16px
|
||||
color: #ffffff
|
||||
font-family: 'Inter', sans-serif
|
||||
gap: 2rem
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
justify-content: center
|
||||
padding: 2rem
|
||||
border: 1px solid rgba(255, 255, 255, 0.3)
|
||||
border-radius: 16px
|
||||
color: #ffffff
|
||||
font-family: 'Inter', sans-serif
|
||||
|
||||
.input
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: flex-start
|
||||
width: 100%
|
||||
gap: 0.5rem
|
||||
|
||||
&__label
|
||||
font-family: 'Inter', sans-serif
|
||||
font-size: 1rem
|
||||
font-weight: 500
|
||||
|
||||
&__textarea
|
||||
font-family: 'Inter', sans-serif
|
||||
font-size: 1rem
|
||||
width: calc(100% - 1rem)
|
||||
height: 5rem
|
||||
background-color: color.adjust(vars.$background, $lightness: 10%)
|
||||
border: 1px solid color.adjust(vars.$background, $lightness: 20%)
|
||||
border-radius: 8px
|
||||
padding: 0.5rem
|
||||
color: color.adjust(vars.$background, $lightness: 60%)
|
||||
outline: none
|
||||
transition: all 0.4s ease-out
|
||||
|
||||
&:hover
|
||||
border-color: color.adjust(vars.$background, $lightness: 30%)
|
||||
|
||||
&:focus
|
||||
border-color: color.adjust(vars.$background, $lightness: 50%)
|
||||
color: color.adjust(vars.$background, $lightness: 80%)
|
||||
|
||||
.output
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: flex-start
|
||||
width: 100%
|
||||
gap: 0.5rem
|
||||
|
||||
&__title
|
||||
margin: 2rem 0 0.5rem 0
|
||||
|
||||
&__description
|
||||
margin: 0 0 1rem 0
|
||||
font-size: 0.8rem
|
||||
color: color.adjust(vars.$background, $lightness: 50%)
|
||||
|
||||
.mini-card
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: flex-start
|
||||
gap: 0.5rem
|
||||
width: 40rem
|
||||
padding: 0.5rem
|
||||
background-color: color.adjust(vars.$background, $lightness: 5%)
|
||||
border-radius: 8px
|
||||
overflow: hidden
|
||||
|
||||
&__label
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: space-between
|
||||
// align-items: center
|
||||
gap: 0.5rem
|
||||
font-size: 0.9rem
|
||||
width: 100%
|
||||
text-align: left
|
||||
|
||||
&__text
|
||||
font-weight: 600
|
||||
background-color: vars.$background
|
||||
border-radius: 0px 0 8px 0
|
||||
color: color.adjust(vars.$background, $lightness: 80%)
|
||||
margin: -0.5rem 0 0.5rem -0.5rem
|
||||
padding: 0.5rem 2rem 0.5rem 1rem
|
||||
|
||||
&__button
|
||||
// border: 1px solid color.adjust(vars.$background, $lightness: 30%)
|
||||
// border-radius: 8px
|
||||
// margin: -0.5rem -0.5rem
|
||||
// padding: 0.2rem 0.2rem
|
||||
|
||||
cursor: pointer
|
||||
|
||||
&__content
|
||||
font-size: 1rem
|
||||
color: color.adjust(vars.$background, $lightness: 70%)
|
||||
padding: 0 0.5rem 0.5rem 0
|
||||
word-break: break-all
|
||||
white-space: pre-wrap
|
||||
overflow-x: auto
|
||||
text-align: left
|
||||
letter-spacing: 0.5px
|
||||
132
src/components/Hash-Generator/HashGeneratorComponent.tsx
Normal file
132
src/components/Hash-Generator/HashGeneratorComponent.tsx
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import './HashGeneratorComponent.sass'
|
||||
import MiniCard from '../MiniCard/MiniCard.tsx'
|
||||
import { TextField } from '@kobalte/core/text-field'
|
||||
import { createSignal, createEffect } from 'solid-js'
|
||||
import { FiCopy } from 'solid-icons/fi'
|
||||
import * as cjs from 'crypto-js'
|
||||
import Alert from '../Alert/Alert.tsx'
|
||||
|
||||
const { MD5, SHA1, SHA224, SHA256, SHA384, SHA512, SHA3, HmacMD5, HmacSHA1, HmacSHA224, HmacSHA256, HmacSHA384, HmacSHA512, HmacSHA3 } = cjs
|
||||
|
||||
export default () => {
|
||||
const [plainText, setPlainText] = createSignal('Change me...')
|
||||
const [passphrase, setPassphrase] = createSignal('This is a sample secret passphrase, change me...')
|
||||
const [md5, setMd5] = createSignal('')
|
||||
const [md6, setMd6] = createSignal('')
|
||||
const [sha1, setSha1] = createSignal('')
|
||||
const [sha224, setSha224] = createSignal('')
|
||||
const [sha256, setSha256] = createSignal('')
|
||||
const [sha384, setSha384] = createSignal('')
|
||||
const [sha512, setSha512] = createSignal('')
|
||||
const [sha3, setSha3] = createSignal('')
|
||||
const [hmacMd5, setHmacmd5] = createSignal('')
|
||||
const [hmacSha1, setHmacsha1] = createSignal('')
|
||||
const [hmacSha224, setHmacsha224] = createSignal('')
|
||||
const [hmacSha256, setHmacsha256] = createSignal('')
|
||||
const [hmacSha384, setHmacsha384] = createSignal('')
|
||||
const [hmacSha512, setHmacsha512] = createSignal('')
|
||||
const [hmacSha3, setHmacsha3] = createSignal('')
|
||||
|
||||
const [pepper, setPepper] = createSignal('')
|
||||
const [argon2, setArgon2] = createSignal('')
|
||||
const [bcrypt, setBcrypt] = createSignal('')
|
||||
const [scrypt, setScrypt] = createSignal('')
|
||||
const [PBKDF2, setPBKDF2] = createSignal('')
|
||||
|
||||
const [copiedText, setCopiedText] = createSignal('')
|
||||
|
||||
const hashingPlainText = () => {
|
||||
setMd5(MD5(plainText()).toString())
|
||||
setSha1(SHA1(plainText()).toString())
|
||||
setSha224(SHA224(plainText()).toString())
|
||||
setSha256(SHA256(plainText()).toString())
|
||||
setSha384(SHA384(plainText()).toString())
|
||||
setSha512(SHA512(plainText()).toString())
|
||||
setSha3(SHA3(plainText()).toString())
|
||||
|
||||
setHmacmd5(HmacMD5(plainText(), passphrase()).toString())
|
||||
setHmacsha1(HmacSHA1(plainText(), passphrase()).toString())
|
||||
setHmacsha224(HmacSHA224(plainText(), passphrase()).toString())
|
||||
setHmacsha256(HmacSHA256(plainText(), passphrase()).toString())
|
||||
setHmacsha384(HmacSHA384(plainText(), passphrase()).toString())
|
||||
setHmacsha512(HmacSHA512(plainText(), passphrase()).toString())
|
||||
setHmacsha3(HmacSHA3(plainText(), passphrase()).toString())
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
hashingPlainText()
|
||||
})
|
||||
|
||||
const copyToClipboard = async (text: string, alertType: string) => {
|
||||
try {
|
||||
navigator.clipboard.writeText(text)
|
||||
setCopiedText(alertType)
|
||||
setTimeout(() => setCopiedText(''), 2000)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section class="section">
|
||||
<TextField class="input" value={plainText()} onChange={setPlainText}>
|
||||
<TextField.Label class="input__label">Plain Text:</TextField.Label>
|
||||
<TextField.TextArea class="input__textarea" />
|
||||
</TextField>
|
||||
|
||||
<div class="output">
|
||||
{/* <span class="output__title">Results:</span> */}
|
||||
|
||||
<MiniCard text="MD5" content={md5()} onClick={() => copyToClipboard(md5(), 'MD5')} />
|
||||
<Alert for="MD5" trigger={copiedText() === 'MD5'} />
|
||||
|
||||
<MiniCard text="SHA1" content={sha1()} onClick={() => copyToClipboard(sha1(), 'SHA1')} />
|
||||
<Alert for="SHA1" trigger={copiedText() === 'SHA1'} />
|
||||
|
||||
<MiniCard text="SHA224" content={sha224()} onClick={() => copyToClipboard(sha224(), 'SHA224')} />
|
||||
<Alert for="SHA224" trigger={copiedText() === 'SHA224'} />
|
||||
|
||||
<MiniCard text="SHA256" content={sha256()} onClick={() => copyToClipboard(sha256(), 'SHA256')} />
|
||||
<Alert for="SHA256" trigger={copiedText() === 'SHA256'} />
|
||||
|
||||
<MiniCard text="SHA384" content={sha384()} onClick={() => copyToClipboard(sha384(), 'SHA384')} />
|
||||
<Alert for="SHA384" trigger={copiedText() === 'SHA384'} />
|
||||
|
||||
<MiniCard text="SHA512" content={sha512()} onClick={() => copyToClipboard(sha512(), 'SHA512')} />
|
||||
<Alert for="SHA512" trigger={copiedText() === 'SHA512'} />
|
||||
|
||||
<MiniCard text="SHA3" content={sha3()} onClick={() => copyToClipboard(sha3(), 'SHA3')} />
|
||||
<Alert for="SHA3" trigger={copiedText() === 'SHA3'} />
|
||||
|
||||
<span class="output__title">with HMAC:</span>
|
||||
<span class="output__description">Hash-based Message Authentication Code (HMAC) needs a secret passphrase to generate a hash.</span>
|
||||
|
||||
<TextField class="input" value={passphrase()} onChange={setPassphrase}>
|
||||
<TextField.Label class="input__label">Secret Passphrase:</TextField.Label>
|
||||
<TextField.TextArea class="input__textarea" />
|
||||
</TextField>
|
||||
|
||||
<MiniCard text="HMAC MD5" content={hmacMd5()} onClick={() => copyToClipboard(hmacMd5(), 'HMAC MD5')} />
|
||||
<Alert for="HMAC MD5" trigger={copiedText() === 'HMAC MD5'} />
|
||||
|
||||
<MiniCard text="HMAC SHA1" content={hmacSha1()} onClick={() => copyToClipboard(hmacSha1(), 'HMAC SHA1')} />
|
||||
<Alert for="HMAC SHA1" trigger={copiedText() === 'HMAC SHA1'} />
|
||||
|
||||
<MiniCard text="HMAC SHA224" content={hmacSha224()} onClick={() => copyToClipboard(hmacSha224(), 'HMAC SHA224')} />
|
||||
<Alert for="HMAC SHA224" trigger={copiedText() === 'HMAC SHA224'} />
|
||||
|
||||
<MiniCard text="HMAC SHA256" content={hmacSha256()} onClick={() => copyToClipboard(hmacSha256(), 'HMAC SHA256')} />
|
||||
<Alert for="HMAC SHA256" trigger={copiedText() === 'HMAC SHA256'} />
|
||||
|
||||
<MiniCard text="HMAC SHA384" content={hmacSha384()} onClick={() => copyToClipboard(hmacSha384(), 'HMAC SHA384')} />
|
||||
<Alert for="HMAC SHA384" trigger={copiedText() === 'HMAC SHA384'} />
|
||||
|
||||
<MiniCard text="HMAC SHA512" content={hmacSha512()} onClick={() => copyToClipboard(hmacSha512(), 'HMAC SHA512')} />
|
||||
<Alert for="HMAC SHA512" trigger={copiedText() === 'HMAC SHA512'} />
|
||||
|
||||
<MiniCard text="HMAC SHA3" content={hmacSha3()} onClick={() => copyToClipboard(hmacSha3(), 'HMAC SHA3')} />
|
||||
<Alert for="HMAC SHA3" trigger={copiedText() === 'HMAC SHA3'} />
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
56
src/components/Input/Input.sass
Normal file
56
src/components/Input/Input.sass
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
@use '/src/assets/css/variables.sass' as vars
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
$backgroundColor: color.adjust(vars.$background, $lightness: 10%)
|
||||
$border: 1px solid color.adjust(vars.$background, $lightness: 20%)
|
||||
|
||||
.input
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: flex-start
|
||||
width: 100%
|
||||
gap: 0.5rem
|
||||
font-family: 'Inter', sans-serif
|
||||
|
||||
&__label
|
||||
font-size: 1rem
|
||||
font-weight: 500
|
||||
|
||||
&__textarea
|
||||
font-size: 1rem
|
||||
width: calc(100% - 1rem)
|
||||
height: 5rem
|
||||
background-color: $backgroundColor
|
||||
border: $border
|
||||
border-radius: 8px
|
||||
padding: 0.5rem
|
||||
color: color.adjust(vars.$background, $lightness: 60%)
|
||||
outline: none
|
||||
transition: all 0.4s ease-out
|
||||
|
||||
&:hover
|
||||
border-color: color.adjust(vars.$background, $lightness: 30%)
|
||||
|
||||
&:focus
|
||||
border-color: color.adjust(vars.$background, $lightness: 50%)
|
||||
color: color.adjust(vars.$background, $lightness: 80%)
|
||||
|
||||
&__text
|
||||
font-size: 1rem
|
||||
width: calc(100% - 1rem)
|
||||
height: 1rem
|
||||
background-color: $backgroundColor
|
||||
border: $border
|
||||
border-radius: 8px
|
||||
padding: 0.75rem 0.5rem
|
||||
color: color.adjust(vars.$background, $lightness: 60%)
|
||||
outline: none
|
||||
transition: all 0.4s ease-out
|
||||
|
||||
&:hover
|
||||
border-color: color.adjust(vars.$background, $lightness: 30%)
|
||||
|
||||
&:focus
|
||||
border-color: color.adjust(vars.$background, $lightness: 50%)
|
||||
color: color.adjust(vars.$background, $lightness: 80%)
|
||||
17
src/components/Input/Input.tsx
Normal file
17
src/components/Input/Input.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import './Input.sass'
|
||||
import { Show } from 'solid-js'
|
||||
import { TextField } from '@kobalte/core/text-field'
|
||||
|
||||
export default (props: { value: string; onChange: (text:string) => void; label: string; isTextField?: boolean }) => (
|
||||
<>
|
||||
<TextField class="input" value={props.value} onChange={props.onChange}>
|
||||
<TextField.Label class="input__label">{props.label}:</TextField.Label>
|
||||
<Show when={props.isTextField}>
|
||||
<TextField.TextArea class="input__textarea" />
|
||||
</Show>
|
||||
<Show when={!props.isTextField}>
|
||||
<TextField.Input class="input__text" />
|
||||
</Show>
|
||||
</TextField>
|
||||
</>
|
||||
)
|
||||
51
src/components/MiniCard/MiniCard.sass
Normal file
51
src/components/MiniCard/MiniCard.sass
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
@use '/src/assets/css/variables.sass' as vars
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
.mini-card
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: flex-start
|
||||
gap: 0.5rem
|
||||
width: 40rem
|
||||
padding: 0.5rem
|
||||
background-color: color.adjust(vars.$background, $lightness: 5%)
|
||||
border-radius: 8px
|
||||
overflow: hidden
|
||||
|
||||
&__label
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: space-between
|
||||
// align-items: center
|
||||
gap: 0.5rem
|
||||
font-size: 0.9rem
|
||||
width: 100%
|
||||
text-align: left
|
||||
|
||||
&__text
|
||||
font-weight: 600
|
||||
background-color: vars.$background
|
||||
border-radius: 0px 0 8px 0
|
||||
color: color.adjust(vars.$background, $lightness: 80%)
|
||||
margin: -0.5rem 0 0.5rem -0.5rem
|
||||
padding: 0.5rem 2rem 0.5rem 1rem
|
||||
|
||||
&__button
|
||||
cursor: pointer
|
||||
|
||||
&:hover
|
||||
transform: scale(1.2)
|
||||
|
||||
&:active
|
||||
transform: scale(0.9)
|
||||
|
||||
&__content
|
||||
font-size: 1rem
|
||||
color: color.adjust(vars.$background, $lightness: 70%)
|
||||
padding: 0 0.5rem 0.5rem 0
|
||||
word-break: break-all
|
||||
white-space: pre-wrap
|
||||
overflow-x: auto
|
||||
text-align: left
|
||||
letter-spacing: 0.5px
|
||||
14
src/components/MiniCard/MiniCard.tsx
Normal file
14
src/components/MiniCard/MiniCard.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import './MiniCard.sass'
|
||||
import { FiCopy } from 'solid-icons/fi'
|
||||
|
||||
export default (props: { text: string; content: string; onClick?: () => void }) => {
|
||||
return (
|
||||
<section class="mini-card">
|
||||
<div class="mini-card__label">
|
||||
<span class="mini-card__label__text">{props.text}</span>
|
||||
<FiCopy class="mini-card__label__button" onClick={props.onClick} />
|
||||
</div>
|
||||
<span class="mini-card__content">{props.content}</span>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
69
src/components/Navbar/Navbar.sass
Normal file
69
src/components/Navbar/Navbar.sass
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
$background: rgba(15, 66, 114, 0.426)
|
||||
$text: #f1f1f1
|
||||
$coffeeColor: #1a726e
|
||||
|
||||
.navbar
|
||||
display: flex
|
||||
justify-content: left
|
||||
gap: 1.25rem
|
||||
align-items: center
|
||||
// background-color: $background
|
||||
padding: 1.25rem
|
||||
// border-radius: 8px
|
||||
|
||||
.searchbox-container
|
||||
flex-grow: 1
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
display: none
|
||||
|
||||
.menuButton
|
||||
background: none
|
||||
border: none
|
||||
color: $text
|
||||
cursor: pointer
|
||||
font-size: 1rem
|
||||
|
||||
.menu
|
||||
display: flex
|
||||
list-style-type: none
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
& li a
|
||||
color: $text
|
||||
text-decoration: none
|
||||
padding: 0.5rem 1rem
|
||||
|
||||
&.open
|
||||
display: flex
|
||||
|
||||
.icon
|
||||
padding: 0.55rem 0.75rem
|
||||
border-radius: 50%
|
||||
margin: -0.75rem
|
||||
|
||||
&:hover
|
||||
background-color: #425b76
|
||||
|
||||
.buymeacoffee-icon
|
||||
display: none
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
display: block
|
||||
border: 1px solid color.adjust($coffeeColor, $lightness: 20%)
|
||||
border-radius: 16px
|
||||
cursor: pointer
|
||||
padding: 0.5rem 1rem
|
||||
margin-left: auto
|
||||
background-color: $coffeeColor
|
||||
border-radius: 16px
|
||||
|
||||
&:active
|
||||
transform: scale(0.9)
|
||||
|
||||
.buymeacoffee-button
|
||||
display: none
|
||||
33
src/components/Navbar/Navbar.tsx
Normal file
33
src/components/Navbar/Navbar.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import './Navbar.sass'
|
||||
import Button from '../Button/Button'
|
||||
import { createSignal } from 'solid-js'
|
||||
import { FiMenu, FiHome, FiGithub, FiCoffee } from 'solid-icons/fi'
|
||||
import { RiSystemInformationLine } from 'solid-icons/ri'
|
||||
import Searchbox from '../Searchbox/Searchbox.tsx'
|
||||
import { SiBuymeacoffee } from 'solid-icons/si'
|
||||
import { FaSolidCode } from 'solid-icons/fa'
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<nav class="navbar">
|
||||
<div class="icon">
|
||||
<FiMenu color="#ffffff" size={24} cursor="pointer" />
|
||||
</div>
|
||||
<a href="/" class="icon" aria-label="Home page">
|
||||
<FiHome color="#ffffff" size={24} cursor="pointer" />
|
||||
</a>
|
||||
<section class="searchbox-container">
|
||||
<Searchbox />
|
||||
</section>
|
||||
<a target="_blank" href="https://git.patalcala.com/patalcala9/aio-tools" class="icon" aria-label="GitHub repository">
|
||||
<FaSolidCode color="#ffffff" size={24} cursor="pointer" />
|
||||
</a>
|
||||
<a href="/about" class="icon" aria-label="About page">
|
||||
<RiSystemInformationLine color="#ffffff" size={32} cursor="pointer" />
|
||||
</a>
|
||||
|
||||
<SiBuymeacoffee class="buymeacoffee-icon" color="#ffffff" size={32} cursor="pointer" />
|
||||
<Button label="Buy me a coffee" forCoffee></Button>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
@use '/src/assets/css/variables.sass' as vars
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
.section
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: flex-start
|
||||
padding: 2rem
|
||||
border: 1px solid rgba(255, 255, 255, 0.3)
|
||||
border-radius: 16px
|
||||
color: #ffffff
|
||||
font-family: 'Inter', sans-serif
|
||||
gap: 2rem
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
justify-content: center
|
||||
padding: 2rem
|
||||
border: 1px solid rgba(255, 255, 255, 0.3)
|
||||
border-radius: 16px
|
||||
color: #ffffff
|
||||
font-family: 'Inter', sans-serif
|
||||
|
||||
.input
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: flex-start
|
||||
width: 100%
|
||||
gap: 0.5rem
|
||||
|
||||
&__label
|
||||
font-family: 'Inter', sans-serif
|
||||
font-size: 1rem
|
||||
font-weight: 500
|
||||
|
||||
&__textarea
|
||||
font-family: 'Inter', sans-serif
|
||||
font-size: 1rem
|
||||
width: calc(100% - 1rem)
|
||||
height: 5rem
|
||||
background-color: color.adjust(vars.$background, $lightness: 10%)
|
||||
border: 1px solid color.adjust(vars.$background, $lightness: 20%)
|
||||
border-radius: 8px
|
||||
padding: 0.5rem
|
||||
color: color.adjust(vars.$background, $lightness: 60%)
|
||||
outline: none
|
||||
transition: all 0.4s ease-out
|
||||
|
||||
&:hover
|
||||
border-color: color.adjust(vars.$background, $lightness: 30%)
|
||||
|
||||
&:focus
|
||||
border-color: color.adjust(vars.$background, $lightness: 50%)
|
||||
color: color.adjust(vars.$background, $lightness: 80%)
|
||||
|
||||
.output
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: flex-start
|
||||
width: 100%
|
||||
gap: 0.5rem
|
||||
|
||||
&__title
|
||||
margin: 2rem 0 0.5rem 0
|
||||
|
||||
&__description
|
||||
margin: 0 0 1rem 0
|
||||
font-size: 0.8rem
|
||||
color: color.adjust(vars.$background, $lightness: 50%)
|
||||
|
||||
.mini-card
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: flex-start
|
||||
gap: 0.5rem
|
||||
width: 40rem
|
||||
padding: 0.5rem
|
||||
background-color: color.adjust(vars.$background, $lightness: 5%)
|
||||
border-radius: 8px
|
||||
overflow: hidden
|
||||
|
||||
&__label
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: space-between
|
||||
// align-items: center
|
||||
gap: 0.5rem
|
||||
font-size: 0.9rem
|
||||
width: 100%
|
||||
text-align: left
|
||||
|
||||
&__text
|
||||
font-weight: 600
|
||||
background-color: vars.$background
|
||||
border-radius: 0px 0 8px 0
|
||||
color: color.adjust(vars.$background, $lightness: 80%)
|
||||
margin: -0.5rem 0 0.5rem -0.5rem
|
||||
padding: 0.5rem 2rem 0.5rem 1rem
|
||||
|
||||
&__button
|
||||
// border: 1px solid color.adjust(vars.$background, $lightness: 30%)
|
||||
// border-radius: 8px
|
||||
// margin: -0.5rem -0.5rem
|
||||
// padding: 0.2rem 0.2rem
|
||||
|
||||
cursor: pointer
|
||||
|
||||
&__content
|
||||
font-size: 1rem
|
||||
color: color.adjust(vars.$background, $lightness: 70%)
|
||||
padding: 0 0.5rem 0.5rem 0
|
||||
word-break: break-all
|
||||
white-space: pre-wrap
|
||||
overflow-x: auto
|
||||
text-align: left
|
||||
letter-spacing: 0.5px
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
import './HashGeneratorComponent.sass'
|
||||
import MiniCard from '../../MiniCard/MiniCard.tsx'
|
||||
import { TextField } from '@kobalte/core/text-field'
|
||||
import { createSignal, createEffect } from 'solid-js'
|
||||
import { FiCopy } from 'solid-icons/fi'
|
||||
import * as cjs from 'crypto-js'
|
||||
import Alert from '../../Alert/Alert.tsx'
|
||||
import Input from '../../Input/Input.tsx'
|
||||
|
||||
const { MD5, SHA1, SHA224, SHA256, SHA384, SHA512, SHA3, HmacMD5, HmacSHA1, HmacSHA224, HmacSHA256, HmacSHA384, HmacSHA512, HmacSHA3 } = cjs
|
||||
|
||||
export default () => {
|
||||
const [plainText, setPlainText] = createSignal('Change me...')
|
||||
const [passphrase, setPassphrase] = createSignal('This is a sample secret passphrase, change me...')
|
||||
const [md5, setMd5] = createSignal('')
|
||||
const [md6, setMd6] = createSignal('')
|
||||
const [sha1, setSha1] = createSignal('')
|
||||
const [sha224, setSha224] = createSignal('')
|
||||
const [sha256, setSha256] = createSignal('')
|
||||
const [sha384, setSha384] = createSignal('')
|
||||
const [sha512, setSha512] = createSignal('')
|
||||
const [sha3, setSha3] = createSignal('')
|
||||
const [hmacMd5, setHmacmd5] = createSignal('')
|
||||
const [hmacSha1, setHmacsha1] = createSignal('')
|
||||
const [hmacSha224, setHmacsha224] = createSignal('')
|
||||
const [hmacSha256, setHmacsha256] = createSignal('')
|
||||
const [hmacSha384, setHmacsha384] = createSignal('')
|
||||
const [hmacSha512, setHmacsha512] = createSignal('')
|
||||
const [hmacSha3, setHmacsha3] = createSignal('')
|
||||
|
||||
const [pepper, setPepper] = createSignal('')
|
||||
const [argon2, setArgon2] = createSignal('')
|
||||
const [bcrypt, setBcrypt] = createSignal('')
|
||||
const [scrypt, setScrypt] = createSignal('')
|
||||
const [PBKDF2, setPBKDF2] = createSignal('')
|
||||
|
||||
const [copiedText, setCopiedText] = createSignal('')
|
||||
|
||||
const hashingPlainText = () => {
|
||||
setMd5(MD5(plainText()).toString())
|
||||
setSha1(SHA1(plainText()).toString())
|
||||
setSha224(SHA224(plainText()).toString())
|
||||
setSha256(SHA256(plainText()).toString())
|
||||
setSha384(SHA384(plainText()).toString())
|
||||
setSha512(SHA512(plainText()).toString())
|
||||
setSha3(SHA3(plainText()).toString())
|
||||
|
||||
setHmacmd5(HmacMD5(plainText(), passphrase()).toString())
|
||||
setHmacsha1(HmacSHA1(plainText(), passphrase()).toString())
|
||||
setHmacsha224(HmacSHA224(plainText(), passphrase()).toString())
|
||||
setHmacsha256(HmacSHA256(plainText(), passphrase()).toString())
|
||||
setHmacsha384(HmacSHA384(plainText(), passphrase()).toString())
|
||||
setHmacsha512(HmacSHA512(plainText(), passphrase()).toString())
|
||||
setHmacsha3(HmacSHA3(plainText(), passphrase()).toString())
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
hashingPlainText()
|
||||
})
|
||||
|
||||
const copyToClipboard = async (text: string, alertType: string) => {
|
||||
try {
|
||||
navigator.clipboard.writeText(text)
|
||||
setCopiedText(alertType)
|
||||
setTimeout(() => setCopiedText(''), 2000)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section class="section">
|
||||
<TextField class="input" value={plainText()} onChange={setPlainText}>
|
||||
<TextField.Label class="input__label">Plain Text:</TextField.Label>
|
||||
<TextField.TextArea class="input__textarea" />
|
||||
</TextField>
|
||||
|
||||
<Input label="Plain Text" value={plainText()} onChange={setPlainText} isTextField></Input>
|
||||
|
||||
<div class="output">
|
||||
{/* <span class="output__title">Results:</span> */}
|
||||
|
||||
<MiniCard text="MD5" content={md5()} onClick={() => copyToClipboard(md5(), 'MD5')} />
|
||||
<Alert for="MD5" trigger={copiedText() === 'MD5'} />
|
||||
|
||||
<MiniCard text="SHA1" content={sha1()} onClick={() => copyToClipboard(sha1(), 'SHA1')} />
|
||||
<Alert for="SHA1" trigger={copiedText() === 'SHA1'} />
|
||||
|
||||
<MiniCard text="SHA224" content={sha224()} onClick={() => copyToClipboard(sha224(), 'SHA224')} />
|
||||
<Alert for="SHA224" trigger={copiedText() === 'SHA224'} />
|
||||
|
||||
<MiniCard text="SHA256" content={sha256()} onClick={() => copyToClipboard(sha256(), 'SHA256')} />
|
||||
<Alert for="SHA256" trigger={copiedText() === 'SHA256'} />
|
||||
|
||||
<MiniCard text="SHA384" content={sha384()} onClick={() => copyToClipboard(sha384(), 'SHA384')} />
|
||||
<Alert for="SHA384" trigger={copiedText() === 'SHA384'} />
|
||||
|
||||
<MiniCard text="SHA512" content={sha512()} onClick={() => copyToClipboard(sha512(), 'SHA512')} />
|
||||
<Alert for="SHA512" trigger={copiedText() === 'SHA512'} />
|
||||
|
||||
<MiniCard text="SHA3" content={sha3()} onClick={() => copyToClipboard(sha3(), 'SHA3')} />
|
||||
<Alert for="SHA3" trigger={copiedText() === 'SHA3'} />
|
||||
|
||||
<span class="output__title">with HMAC:</span>
|
||||
<span class="output__description">Hash-based Message Authentication Code (HMAC) needs a secret passphrase to generate a hash.</span>
|
||||
|
||||
<TextField class="input" value={passphrase()} onChange={setPassphrase} defaultValue={passphrase()}>
|
||||
<TextField.Label class="input__label">Secret Passphrase:</TextField.Label>
|
||||
<TextField.TextArea class="input__textarea" />
|
||||
</TextField>
|
||||
|
||||
<MiniCard text="HMAC MD5" content={hmacMd5()} onClick={() => copyToClipboard(hmacMd5(), 'HMAC MD5')} />
|
||||
<Alert for="HMAC MD5" trigger={copiedText() === 'HMAC MD5'} />
|
||||
|
||||
<MiniCard text="HMAC SHA1" content={hmacSha1()} onClick={() => copyToClipboard(hmacSha1(), 'HMAC SHA1')} />
|
||||
<Alert for="HMAC SHA1" trigger={copiedText() === 'HMAC SHA1'} />
|
||||
|
||||
<MiniCard text="HMAC SHA224" content={hmacSha224()} onClick={() => copyToClipboard(hmacSha224(), 'HMAC SHA224')} />
|
||||
<Alert for="HMAC SHA224" trigger={copiedText() === 'HMAC SHA224'} />
|
||||
|
||||
<MiniCard text="HMAC SHA256" content={hmacSha256()} onClick={() => copyToClipboard(hmacSha256(), 'HMAC SHA256')} />
|
||||
<Alert for="HMAC SHA256" trigger={copiedText() === 'HMAC SHA256'} />
|
||||
|
||||
<MiniCard text="HMAC SHA384" content={hmacSha384()} onClick={() => copyToClipboard(hmacSha384(), 'HMAC SHA384')} />
|
||||
<Alert for="HMAC SHA384" trigger={copiedText() === 'HMAC SHA384'} />
|
||||
|
||||
<MiniCard text="HMAC SHA512" content={hmacSha512()} onClick={() => copyToClipboard(hmacSha512(), 'HMAC SHA512')} />
|
||||
<Alert for="HMAC SHA512" trigger={copiedText() === 'HMAC SHA512'} />
|
||||
|
||||
<MiniCard text="HMAC SHA3" content={hmacSha3()} onClick={() => copyToClipboard(hmacSha3(), 'HMAC SHA3')} />
|
||||
<Alert for="HMAC SHA3" trigger={copiedText() === 'HMAC SHA3'} />
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
@use '/src/assets/css/variables.sass' as vars
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
.section
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: flex-start
|
||||
padding: 2rem
|
||||
border: vars.$componentBorder
|
||||
border-radius: vars.$componentBorderRadius
|
||||
color: #ffffff
|
||||
font-family: 'Inter', sans-serif
|
||||
gap: 2rem
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import './PasswordManagerComponent.sass'
|
||||
import { AiTwotonePlusCircle } from 'solid-icons/ai'
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<section class="section">
|
||||
<div>1</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
@use '/src/assets/css/variables.sass' as vars
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
$toggleOffColor: rgba(126, 206, 241, 0.286)
|
||||
$toggleOnColor: rgba(53, 155, 80, 0.993)
|
||||
|
||||
.section
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: flex-start
|
||||
padding: 2rem
|
||||
border: 1px solid rgba(255, 255, 255, 0.3)
|
||||
border-radius: 16px
|
||||
color: #ffffff
|
||||
font-family: 'Inter', sans-serif
|
||||
gap: 2rem
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
justify-content: center
|
||||
padding: 2rem
|
||||
border: 1px solid rgba(255, 255, 255, 0.3)
|
||||
border-radius: 16px
|
||||
color: #ffffff
|
||||
font-family: 'Inter', sans-serif
|
||||
|
||||
.toggle--group
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: flex-start
|
||||
align-items: flex-start
|
||||
padding: 0 2rem 0 0
|
||||
margin: 0
|
||||
|
||||
.right--side
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
.slider--group
|
||||
// grid-area: slider--group
|
||||
padding: 0 0 0 1rem
|
||||
flex-grow: 0
|
||||
|
||||
.display--group
|
||||
padding: 3rem 1rem 1rem 1rem
|
||||
|
||||
.toggle
|
||||
color: white
|
||||
padding: 0.25rem 0
|
||||
display: flex
|
||||
flex-direction: row
|
||||
gap: 0.5rem
|
||||
align-items: center
|
||||
height: auto
|
||||
|
||||
&__text
|
||||
font-size: 1rem
|
||||
|
||||
&__switch
|
||||
visibility: hidden
|
||||
width: 0
|
||||
height: 0
|
||||
|
||||
&:checked + label
|
||||
background-color: $toggleOnColor
|
||||
transition: background-color 0.5s ease-in-out
|
||||
|
||||
&:checked + label:after
|
||||
left: calc(100% - 6px)
|
||||
transform: translateX(-70%)
|
||||
|
||||
.password-display
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: flex-start
|
||||
align-items: flex-start
|
||||
color: #ffffff
|
||||
// margin-top: 20px
|
||||
// border: 1px solid #ffffff
|
||||
// padding: 1rem
|
||||
// border-radius: 5px
|
||||
// width: 30rem
|
||||
|
||||
&__label
|
||||
font-weight: bold
|
||||
margin: 1rem 0 -0.5rem 0
|
||||
|
||||
&__content
|
||||
border: 1px solid color.adjust(vars.$background, $lightness: 20%)
|
||||
background-color: color.adjust(vars.$background, $lightness: 5%)
|
||||
color: color.adjust(vars.$background, $lightness: 80%)
|
||||
width: 23rem
|
||||
height: auto
|
||||
padding: 1rem
|
||||
border-radius: 5px
|
||||
text-align: left
|
||||
word-wrap: break-word
|
||||
letter-spacing: 1px
|
||||
font-size: 1rem
|
||||
font-weight: 500
|
||||
// font-family: 'Inter'
|
||||
|
||||
.button--group
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: flex-start
|
||||
gap: 1rem
|
||||
// margin-top: 20px
|
||||
// border: 1px solid #ffffff
|
||||
// padding: 1rem
|
||||
// border-radius: 5px
|
||||
width: 30rem
|
||||
margin: 1rem 0 0 0
|
||||
|
||||
.slider
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
user-select: none
|
||||
touch-action: none
|
||||
margin: 1rem 0 0 0
|
||||
gap: 0.5rem
|
||||
width: 41rem
|
||||
cursor: pointer
|
||||
|
||||
&__label
|
||||
display: flex
|
||||
flex-direction: row
|
||||
gap: 0.5rem
|
||||
width: 100%
|
||||
|
||||
&__track
|
||||
background-color: hsl(240 6% 90%)
|
||||
position: relative
|
||||
border-radius: 9999px
|
||||
height: 8px
|
||||
width: 100%
|
||||
|
||||
&__range
|
||||
position: absolute
|
||||
background-color: $toggleOnColor
|
||||
border-radius: 9999px
|
||||
height: 100%
|
||||
|
||||
&__thumb
|
||||
display: block
|
||||
width: 16px
|
||||
height: 16px
|
||||
background-color: $toggleOnColor
|
||||
border-radius: 9999px
|
||||
top: -4px
|
||||
|
||||
&:hover
|
||||
box-shadow: 0 0 0 5px color.adjust($toggleOnColor, $lightness: 9%)
|
||||
|
||||
&:focus
|
||||
outline: none
|
||||
box-shadow: 0 0 0 5px color.adjust($toggleOnColor, $lightness: 9%)
|
||||
|
||||
.alert
|
||||
border-radius: 6px
|
||||
padding: 1rem 1.25rem
|
||||
background-color: rgba(12, 31, 39, 0.991)
|
||||
border: 1px solid color.adjust($toggleOffColor, $lightness: 2%)
|
||||
color: rgba(126, 206, 241, 0.748)
|
||||
font-size: 1.25rem
|
||||
|
||||
.alert--container
|
||||
position: fixed
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
z-index: 1000
|
||||
|
||||
.checkbox
|
||||
display: inline-flex
|
||||
align-items: center
|
||||
|
||||
&__control
|
||||
height: 20px
|
||||
width: 20px
|
||||
border-radius: 6px
|
||||
background-color: #e5e8ec
|
||||
cursor: pointer
|
||||
|
||||
&__input:focus-visible + &__control
|
||||
// outline: 2px solid hsl(200 98% 39%)
|
||||
outline-offset: 2px
|
||||
|
||||
&__control[data-checked]
|
||||
border-color: color.adjust($toggleOnColor, $blackness: 20%)
|
||||
background-color: $toggleOnColor
|
||||
color: white
|
||||
|
||||
&__label
|
||||
display: none
|
||||
margin-left: 6px
|
||||
color: hsl(240 6% 10%)
|
||||
font-size: 14px
|
||||
user-select: none
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
import './PasswordGeneratorComponent.sass'
|
||||
import { createSignal, createEffect, Show } from 'solid-js'
|
||||
import Button from '../../Button/Button'
|
||||
import MiniCard from '../../MiniCard/MiniCard'
|
||||
// import ToggleSwtich from '../ToggleSwitch/ToggleSwitch'
|
||||
import { Slider } from '@kobalte/core/slider'
|
||||
import { Switch } from '@kobalte/core/switch'
|
||||
import { Alert } from '@kobalte/core/alert'
|
||||
import { Checkbox } from '@kobalte/core/checkbox'
|
||||
import { FaSolidCheck } from 'solid-icons/fa'
|
||||
|
||||
export default () => {
|
||||
const [uppercase, setUppercase] = createSignal(true)
|
||||
const [lowercase, setLowercase] = createSignal(true)
|
||||
const [numbers, setNumbers] = createSignal(true)
|
||||
const [symbols, setSymbols] = createSignal(true)
|
||||
const [emojis, setEmojis] = createSignal(false)
|
||||
const [length, setLength] = createSignal(8)
|
||||
const [password, setPassword] = createSignal('')
|
||||
const [showAlert, setShowAlert] = createSignal(false)
|
||||
|
||||
const generatePassword = () => {
|
||||
let characters = ''
|
||||
if (uppercase()) characters += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
if (lowercase()) characters += 'abcdefghijklmnopqrstuvwxyz'
|
||||
if (numbers()) characters += '0123456789'
|
||||
if (symbols()) characters += '!@#$%^&*()_+-=[]{}|;:,.<>?~'
|
||||
if (emojis()) characters += '😊🙂😅🤩😜😝🐯🐱🐵🦊🦁🐷🐮🧭'
|
||||
|
||||
let passwordArray = Array.from({ length: length() }, () => characters[Math.floor(Math.random() * characters.length)])
|
||||
setPassword(passwordArray.join(''))
|
||||
}
|
||||
|
||||
// Effect to update the display when the length changes
|
||||
createEffect(() => {
|
||||
generatePassword()
|
||||
})
|
||||
|
||||
const handleToggleChange = (target: string) => {
|
||||
if (target === 'uppercase') setUppercase(!uppercase())
|
||||
else if (target === 'lowercase') setLowercase(!lowercase())
|
||||
else if (target === 'numbers') setNumbers(!numbers())
|
||||
else if (target === 'symbols') setSymbols(!symbols())
|
||||
else if (target === 'emojis') setEmojis(!emojis())
|
||||
|
||||
generatePassword()
|
||||
}
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
try {
|
||||
navigator.clipboard.writeText(password())
|
||||
setShowAlert(true)
|
||||
setTimeout(() => setShowAlert(false), 2000)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section class="section">
|
||||
<div class="toggle--group">
|
||||
<h3>Options:</h3>
|
||||
|
||||
<div class="toggle">
|
||||
<Checkbox class="checkbox" checked={uppercase()} onChange={setUppercase}>
|
||||
<Checkbox.Input class="checkbox__input" />
|
||||
<Checkbox.Control class="checkbox__control">
|
||||
<Checkbox.Indicator>
|
||||
<FaSolidCheck size={12} />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox.Control>
|
||||
<Checkbox.Label class="checkbox__label">Subscribe</Checkbox.Label>
|
||||
</Checkbox>
|
||||
<span class="toggle__text">Uppercase</span>
|
||||
{/* <input class="toggle__switch" type="checkbox" id="uppercase" />
|
||||
<label for="uppercase" onClick={() => handleToggleChange('uppercase')}></label> */}
|
||||
</div>
|
||||
|
||||
<div class="toggle">
|
||||
<Checkbox class="checkbox" checked={lowercase()} onChange={setLowercase}>
|
||||
<Checkbox.Input class="checkbox__input" />
|
||||
<Checkbox.Control class="checkbox__control">
|
||||
<Checkbox.Indicator>
|
||||
<FaSolidCheck size={12} />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox.Control>
|
||||
<Checkbox.Label class="checkbox__label">Subscribe</Checkbox.Label>
|
||||
</Checkbox>
|
||||
<span class="toggle__text">Lowercase</span>
|
||||
{/* <input class="toggle__switch" type="checkbox" id="lowercase" />
|
||||
<label for="lowercase" onClick={() => handleToggleChange('lowercase')}></label> */}
|
||||
</div>
|
||||
<div class="toggle">
|
||||
<Checkbox class="checkbox" checked={numbers()} onChange={setNumbers}>
|
||||
<Checkbox.Input class="checkbox__input" />
|
||||
<Checkbox.Control class="checkbox__control">
|
||||
<Checkbox.Indicator>
|
||||
<FaSolidCheck size={12} />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox.Control>
|
||||
<Checkbox.Label class="checkbox__label">Subscribe</Checkbox.Label>
|
||||
</Checkbox>
|
||||
<span class="toggle__text">Numbers</span>
|
||||
{/* <input class="toggle__switch" type="checkbox" id="numbers" />
|
||||
<label for="numbers" onClick={() => handleToggleChange('numbers')}></label> */}
|
||||
</div>
|
||||
<div class="toggle">
|
||||
<Checkbox class="checkbox" checked={symbols()} onChange={setSymbols}>
|
||||
<Checkbox.Input class="checkbox__input" />
|
||||
<Checkbox.Control class="checkbox__control">
|
||||
<Checkbox.Indicator>
|
||||
<FaSolidCheck size={12} />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox.Control>
|
||||
<Checkbox.Label class="checkbox__label">Subscribe</Checkbox.Label>
|
||||
</Checkbox>
|
||||
<span class="toggle__text">Symbols</span>
|
||||
{/* <input class="toggle__switch" type="checkbox" id="symbols" />
|
||||
<label for="symbols" onClick={() => handleToggleChange('symbols')}></label> */}
|
||||
</div>
|
||||
{/* <div class="toggle">
|
||||
<span class="toggle__text">Emojis:</span>
|
||||
<input class="toggle__switch" type="checkbox" id="emojis" />
|
||||
<label for="emojis" onClick={() => handleToggleChange('emojis')}></label>
|
||||
</div> */}
|
||||
<div class="toggle">
|
||||
<Checkbox class="checkbox" checked={emojis()} onChange={setEmojis}>
|
||||
<Checkbox.Input class="checkbox__input" />
|
||||
<Checkbox.Control class="checkbox__control">
|
||||
<Checkbox.Indicator>
|
||||
<FaSolidCheck size={12} />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox.Control>
|
||||
<Checkbox.Label class="checkbox__label">Subscribe</Checkbox.Label>
|
||||
</Checkbox>
|
||||
<span class="toggle__text">Emojis</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="right--side">
|
||||
<div class="slider--group">
|
||||
<Slider class="slider" minValue={8} maxValue={60} onChange={setLength}>
|
||||
<div class="slider__label">
|
||||
<span>Length: </span>
|
||||
<Slider.ValueLabel />
|
||||
</div>
|
||||
<Slider.Track class="slider__track">
|
||||
<Slider.Fill class="slider__range" />
|
||||
<Slider.Thumb class="slider__thumb">
|
||||
<Slider.Input />
|
||||
</Slider.Thumb>
|
||||
</Slider.Track>
|
||||
</Slider>
|
||||
</div>
|
||||
|
||||
<div class="display--group">
|
||||
{/* <div class="password-display">
|
||||
<p class="password-display__label">Generated Password:</p>
|
||||
<p class="password-display__content">{password()}</p>
|
||||
</div> */}
|
||||
|
||||
<MiniCard text="Generated Password" content={password()} onClick={copyToClipboard} />
|
||||
{/* <MiniCard text="Generated Password" content={password()} onClick={() => copyToClipboard()} alignLeft={true} /> */}
|
||||
|
||||
<div class="button--group">
|
||||
{/* <Button label="Copy" onClick={() => copyToClipboard()} /> */}
|
||||
<Button label="Generate Again" onClick={() => generatePassword()} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="alert--container">
|
||||
<Show when={showAlert()}>
|
||||
<Alert class="alert alert--float">Copied to clipboard!</Alert>
|
||||
</Show>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
@use '/src/assets/css/variables.sass' as vars
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
.section
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: flex-start
|
||||
padding: 2rem
|
||||
border: vars.$componentBorder
|
||||
border-radius: vars.$componentBorderRadius
|
||||
color: #ffffff
|
||||
font-family: 'Inter', sans-serif
|
||||
gap: 2rem
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import './PasswordManagerComponent.sass'
|
||||
import { AiTwotonePlusCircle } from 'solid-icons/ai'
|
||||
import { createSignal, createEffect } from 'solid-js'
|
||||
import Input from '../../Input/Input'
|
||||
import AddItem from '../../AddItem/AddItem.tsx'
|
||||
|
||||
const [passwords, setPasswords] = createSignal('')
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<section class="section">
|
||||
<div>
|
||||
<Input label="Username" value={passwords()} onChange={setPasswords} />
|
||||
<AddItem />
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
14
src/components/PageTitle/PageTitle.sass
Normal file
14
src/components/PageTitle/PageTitle.sass
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
.title
|
||||
color: #ffffff
|
||||
|
||||
&__text
|
||||
font-size: clamp(1.2rem, 5vw, 2.4rem)
|
||||
margin-bottom: 1rem
|
||||
|
||||
&__description
|
||||
font-size: clamp(0.5rem, 2vw, 1rem)
|
||||
opacity: 0.6
|
||||
margin-bottom: 3rem
|
||||
|
||||
&__icon
|
||||
font-size: clamp(2rem, 5vw, 4rem)
|
||||
23
src/components/PageTitle/PageTitle.tsx
Normal file
23
src/components/PageTitle/PageTitle.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import './PageTitle.sass'
|
||||
import { Show } from 'solid-js'
|
||||
import { RiSystemLockPasswordLine } from 'solid-icons/ri'
|
||||
import { FiHash } from 'solid-icons/fi'
|
||||
import { Si1password } from 'solid-icons/si'
|
||||
|
||||
export default (props: { title: string; description: string }) => {
|
||||
return (
|
||||
<section class="title">
|
||||
<Show when={props.title === 'Password Generator'}>
|
||||
<RiSystemLockPasswordLine class="title__icon" opacity={1} />
|
||||
</Show>
|
||||
<Show when={props.title === 'Hash Generator'}>
|
||||
<FiHash class="title__icon" opacity={1} />
|
||||
</Show>
|
||||
<Show when={props.title === 'Password Manager'}>
|
||||
<Si1password class="title__icon" opacity={1} />
|
||||
</Show>
|
||||
<h1 class="title__text">{props.title}</h1>
|
||||
<p class="title__description">{props.description}</p>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
@use '/src/assets/css/variables.sass' as vars
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use 'sass:color'
|
||||
|
||||
$toggleOffColor: rgba(126, 206, 241, 0.286)
|
||||
$toggleOnColor: rgba(53, 155, 80, 0.993)
|
||||
|
||||
.section
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: flex-start
|
||||
padding: 2rem
|
||||
border: 1px solid rgba(255, 255, 255, 0.3)
|
||||
border-radius: 16px
|
||||
color: #ffffff
|
||||
font-family: 'Inter', sans-serif
|
||||
gap: 2rem
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
justify-content: center
|
||||
padding: 2rem
|
||||
border: 1px solid rgba(255, 255, 255, 0.3)
|
||||
border-radius: 16px
|
||||
color: #ffffff
|
||||
font-family: 'Inter', sans-serif
|
||||
|
||||
.toggle--group
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: flex-start
|
||||
align-items: flex-start
|
||||
padding: 0 2rem 0 0
|
||||
margin: 0
|
||||
|
||||
.right--side
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
.slider--group
|
||||
// grid-area: slider--group
|
||||
padding: 0 0 0 1rem
|
||||
flex-grow: 0
|
||||
|
||||
.display--group
|
||||
padding: 3rem 1rem 1rem 1rem
|
||||
|
||||
.toggle
|
||||
color: white
|
||||
padding: 0.25rem 0
|
||||
display: flex
|
||||
flex-direction: row
|
||||
gap: 0.5rem
|
||||
align-items: center
|
||||
height: auto
|
||||
|
||||
&__text
|
||||
font-size: 1rem
|
||||
|
||||
&__switch
|
||||
visibility: hidden
|
||||
width: 0
|
||||
height: 0
|
||||
|
||||
&:checked + label
|
||||
background-color: $toggleOnColor
|
||||
transition: background-color 0.5s ease-in-out
|
||||
|
||||
&:checked + label:after
|
||||
left: calc(100% - 6px)
|
||||
transform: translateX(-70%)
|
||||
|
||||
.password-display
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: flex-start
|
||||
align-items: flex-start
|
||||
color: #ffffff
|
||||
// margin-top: 20px
|
||||
// border: 1px solid #ffffff
|
||||
// padding: 1rem
|
||||
// border-radius: 5px
|
||||
// width: 30rem
|
||||
|
||||
&__label
|
||||
font-weight: bold
|
||||
margin: 1rem 0 -0.5rem 0
|
||||
|
||||
&__content
|
||||
border: 1px solid color.adjust(vars.$background, $lightness: 20%)
|
||||
background-color: color.adjust(vars.$background, $lightness: 5%)
|
||||
color: color.adjust(vars.$background, $lightness: 80%)
|
||||
width: 23rem
|
||||
height: auto
|
||||
padding: 1rem
|
||||
border-radius: 5px
|
||||
text-align: left
|
||||
word-wrap: break-word
|
||||
letter-spacing: 1px
|
||||
font-size: 1rem
|
||||
font-weight: 500
|
||||
// font-family: 'Inter'
|
||||
|
||||
.button--group
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: flex-start
|
||||
gap: 1rem
|
||||
// margin-top: 20px
|
||||
// border: 1px solid #ffffff
|
||||
// padding: 1rem
|
||||
// border-radius: 5px
|
||||
width: 30rem
|
||||
margin: 1rem 0 0 0
|
||||
|
||||
.slider
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
user-select: none
|
||||
touch-action: none
|
||||
margin: 1rem 0 0 0
|
||||
gap: 0.5rem
|
||||
width: 41rem
|
||||
cursor: pointer
|
||||
|
||||
&__label
|
||||
display: flex
|
||||
flex-direction: row
|
||||
gap: 0.5rem
|
||||
width: 100%
|
||||
|
||||
&__track
|
||||
background-color: hsl(240 6% 90%)
|
||||
position: relative
|
||||
border-radius: 9999px
|
||||
height: 8px
|
||||
width: 100%
|
||||
|
||||
&__range
|
||||
position: absolute
|
||||
background-color: $toggleOnColor
|
||||
border-radius: 9999px
|
||||
height: 100%
|
||||
|
||||
&__thumb
|
||||
display: block
|
||||
width: 16px
|
||||
height: 16px
|
||||
background-color: $toggleOnColor
|
||||
border-radius: 9999px
|
||||
top: -4px
|
||||
|
||||
&:hover
|
||||
box-shadow: 0 0 0 5px color.adjust($toggleOnColor, $lightness: 9%)
|
||||
|
||||
&:focus
|
||||
outline: none
|
||||
box-shadow: 0 0 0 5px color.adjust($toggleOnColor, $lightness: 9%)
|
||||
|
||||
.alert
|
||||
border-radius: 6px
|
||||
padding: 1rem 1.25rem
|
||||
background-color: rgba(12, 31, 39, 0.991)
|
||||
border: 1px solid color.adjust($toggleOffColor, $lightness: 2%)
|
||||
color: rgba(126, 206, 241, 0.748)
|
||||
font-size: 1.25rem
|
||||
|
||||
.alert--container
|
||||
position: fixed
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
z-index: 1000
|
||||
|
||||
.checkbox
|
||||
display: inline-flex
|
||||
align-items: center
|
||||
|
||||
&__control
|
||||
height: 20px
|
||||
width: 20px
|
||||
border-radius: 6px
|
||||
background-color: #e5e8ec
|
||||
cursor: pointer
|
||||
|
||||
&__input:focus-visible + &__control
|
||||
// outline: 2px solid hsl(200 98% 39%)
|
||||
outline-offset: 2px
|
||||
|
||||
&__control[data-checked]
|
||||
border-color: color.adjust($toggleOnColor, $blackness: 20%)
|
||||
background-color: $toggleOnColor
|
||||
color: white
|
||||
|
||||
&__label
|
||||
display: none
|
||||
margin-left: 6px
|
||||
color: hsl(240 6% 10%)
|
||||
font-size: 14px
|
||||
user-select: none
|
||||
179
src/components/Password-Generator/PasswordGeneratorComponent.tsx
Normal file
179
src/components/Password-Generator/PasswordGeneratorComponent.tsx
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
import './PasswordGeneratorComponent.sass'
|
||||
import { createSignal, createEffect, Show } from 'solid-js'
|
||||
import Button from '../Button/Button'
|
||||
import MiniCard from '../MiniCard/MiniCard'
|
||||
// import ToggleSwtich from '../ToggleSwitch/ToggleSwitch'
|
||||
import { Slider } from '@kobalte/core/slider'
|
||||
import { Switch } from '@kobalte/core/switch'
|
||||
import { Alert } from '@kobalte/core/alert'
|
||||
import { Checkbox } from '@kobalte/core/checkbox'
|
||||
import { FaSolidCheck } from 'solid-icons/fa'
|
||||
|
||||
export default () => {
|
||||
const [uppercase, setUppercase] = createSignal(true)
|
||||
const [lowercase, setLowercase] = createSignal(true)
|
||||
const [numbers, setNumbers] = createSignal(true)
|
||||
const [symbols, setSymbols] = createSignal(true)
|
||||
const [emojis, setEmojis] = createSignal(false)
|
||||
const [length, setLength] = createSignal(8)
|
||||
const [password, setPassword] = createSignal('')
|
||||
const [showAlert, setShowAlert] = createSignal(false)
|
||||
|
||||
const generatePassword = () => {
|
||||
let characters = ''
|
||||
if (uppercase()) characters += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
if (lowercase()) characters += 'abcdefghijklmnopqrstuvwxyz'
|
||||
if (numbers()) characters += '0123456789'
|
||||
if (symbols()) characters += '!@#$%^&*()_+-=[]{}|;:,.<>?~'
|
||||
if (emojis()) characters += '😊🙂😅🤩😜😝🐯🐱🐵🦊🦁🐷🐮🧭'
|
||||
|
||||
let passwordArray = Array.from({ length: length() }, () => characters[Math.floor(Math.random() * characters.length)])
|
||||
setPassword(passwordArray.join(''))
|
||||
}
|
||||
|
||||
// Effect to update the display when the length changes
|
||||
createEffect(() => {
|
||||
generatePassword()
|
||||
})
|
||||
|
||||
const handleToggleChange = (target: string) => {
|
||||
if (target === 'uppercase') setUppercase(!uppercase())
|
||||
else if (target === 'lowercase') setLowercase(!lowercase())
|
||||
else if (target === 'numbers') setNumbers(!numbers())
|
||||
else if (target === 'symbols') setSymbols(!symbols())
|
||||
else if (target === 'emojis') setEmojis(!emojis())
|
||||
|
||||
generatePassword()
|
||||
}
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
try {
|
||||
navigator.clipboard.writeText(password())
|
||||
setShowAlert(true)
|
||||
setTimeout(() => setShowAlert(false), 2000)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section class="section">
|
||||
<div class="toggle--group">
|
||||
<h3>Options:</h3>
|
||||
|
||||
<div class="toggle">
|
||||
<Checkbox class="checkbox" checked={uppercase()} onChange={setUppercase}>
|
||||
<Checkbox.Input class="checkbox__input" />
|
||||
<Checkbox.Control class="checkbox__control">
|
||||
<Checkbox.Indicator>
|
||||
<FaSolidCheck size={12} />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox.Control>
|
||||
<Checkbox.Label class="checkbox__label">Subscribe</Checkbox.Label>
|
||||
</Checkbox>
|
||||
<span class="toggle__text">Uppercase</span>
|
||||
{/* <input class="toggle__switch" type="checkbox" id="uppercase" />
|
||||
<label for="uppercase" onClick={() => handleToggleChange('uppercase')}></label> */}
|
||||
</div>
|
||||
|
||||
<div class="toggle">
|
||||
<Checkbox class="checkbox" checked={lowercase()} onChange={setLowercase}>
|
||||
<Checkbox.Input class="checkbox__input" />
|
||||
<Checkbox.Control class="checkbox__control">
|
||||
<Checkbox.Indicator>
|
||||
<FaSolidCheck size={12} />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox.Control>
|
||||
<Checkbox.Label class="checkbox__label">Subscribe</Checkbox.Label>
|
||||
</Checkbox>
|
||||
<span class="toggle__text">Lowercase</span>
|
||||
{/* <input class="toggle__switch" type="checkbox" id="lowercase" />
|
||||
<label for="lowercase" onClick={() => handleToggleChange('lowercase')}></label> */}
|
||||
</div>
|
||||
<div class="toggle">
|
||||
<Checkbox class="checkbox" checked={numbers()} onChange={setNumbers}>
|
||||
<Checkbox.Input class="checkbox__input" />
|
||||
<Checkbox.Control class="checkbox__control">
|
||||
<Checkbox.Indicator>
|
||||
<FaSolidCheck size={12} />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox.Control>
|
||||
<Checkbox.Label class="checkbox__label">Subscribe</Checkbox.Label>
|
||||
</Checkbox>
|
||||
<span class="toggle__text">Numbers</span>
|
||||
{/* <input class="toggle__switch" type="checkbox" id="numbers" />
|
||||
<label for="numbers" onClick={() => handleToggleChange('numbers')}></label> */}
|
||||
</div>
|
||||
<div class="toggle">
|
||||
<Checkbox class="checkbox" checked={symbols()} onChange={setSymbols}>
|
||||
<Checkbox.Input class="checkbox__input" />
|
||||
<Checkbox.Control class="checkbox__control">
|
||||
<Checkbox.Indicator>
|
||||
<FaSolidCheck size={12} />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox.Control>
|
||||
<Checkbox.Label class="checkbox__label">Subscribe</Checkbox.Label>
|
||||
</Checkbox>
|
||||
<span class="toggle__text">Symbols</span>
|
||||
{/* <input class="toggle__switch" type="checkbox" id="symbols" />
|
||||
<label for="symbols" onClick={() => handleToggleChange('symbols')}></label> */}
|
||||
</div>
|
||||
{/* <div class="toggle">
|
||||
<span class="toggle__text">Emojis:</span>
|
||||
<input class="toggle__switch" type="checkbox" id="emojis" />
|
||||
<label for="emojis" onClick={() => handleToggleChange('emojis')}></label>
|
||||
</div> */}
|
||||
<div class="toggle">
|
||||
<Checkbox class="checkbox" checked={emojis()} onChange={setEmojis}>
|
||||
<Checkbox.Input class="checkbox__input" />
|
||||
<Checkbox.Control class="checkbox__control">
|
||||
<Checkbox.Indicator>
|
||||
<FaSolidCheck size={12} />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox.Control>
|
||||
<Checkbox.Label class="checkbox__label">Subscribe</Checkbox.Label>
|
||||
</Checkbox>
|
||||
<span class="toggle__text">Emojis</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="right--side">
|
||||
<div class="slider--group">
|
||||
<Slider class="slider" minValue={8} maxValue={60} onChange={setLength}>
|
||||
<div class="slider__label">
|
||||
<span>Length: </span>
|
||||
<Slider.ValueLabel />
|
||||
</div>
|
||||
<Slider.Track class="slider__track">
|
||||
<Slider.Fill class="slider__range" />
|
||||
<Slider.Thumb class="slider__thumb">
|
||||
<Slider.Input />
|
||||
</Slider.Thumb>
|
||||
</Slider.Track>
|
||||
</Slider>
|
||||
</div>
|
||||
|
||||
<div class="display--group">
|
||||
{/* <div class="password-display">
|
||||
<p class="password-display__label">Generated Password:</p>
|
||||
<p class="password-display__content">{password()}</p>
|
||||
</div> */}
|
||||
|
||||
<MiniCard text="Generated Password" content={password()} onClick={copyToClipboard} />
|
||||
{/* <MiniCard text="Generated Password" content={password()} onClick={() => copyToClipboard()} alignLeft={true} /> */}
|
||||
|
||||
<div class="button--group">
|
||||
{/* <Button label="Copy" onClick={() => copyToClipboard()} /> */}
|
||||
<Button label="Generate Again" onClick={() => generatePassword()} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="alert--container">
|
||||
<Show when={showAlert()}>
|
||||
<Alert class="alert alert--float">Copied to clipboard!</Alert>
|
||||
</Show>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
8
src/components/Searchbox/Searchbox.sass
Normal file
8
src/components/Searchbox/Searchbox.sass
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
.searchbox
|
||||
display: flex
|
||||
align-items: center
|
||||
padding: 0.5rem 1rem
|
||||
gap: 0.75rem
|
||||
background-color: #8686885a
|
||||
border-radius: 0.5rem
|
||||
color: #ffffff
|
||||
11
src/components/Searchbox/Searchbox.tsx
Normal file
11
src/components/Searchbox/Searchbox.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import './Searchbox.sass'
|
||||
import { FiSearch } from 'solid-icons/fi'
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<section class="searchbox">
|
||||
<FiSearch size={16} cursor="pointer" />
|
||||
<span>Search</span>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
53
src/components/ToggleSwitch/ToggleSwitch.sass
Normal file
53
src/components/ToggleSwitch/ToggleSwitch.sass
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
$toggleOffColor: rgba(126, 206, 241, 0.286)
|
||||
$toggleOnColor: rgba(53, 155, 80, 0.993)
|
||||
|
||||
.toggle
|
||||
color: white
|
||||
padding: 1rem
|
||||
display: flex
|
||||
flex-direction: row
|
||||
gap: 0.5rem
|
||||
align-items: center
|
||||
height: auto
|
||||
|
||||
&__text
|
||||
font-size: 1rem
|
||||
|
||||
&__switch[type=checkbox]
|
||||
visibility: hidden
|
||||
width: 0
|
||||
height: 0
|
||||
|
||||
&:checked + label
|
||||
background-color: $toggleOnColor
|
||||
transition: background-color 0.5s ease-in-out
|
||||
|
||||
&:checked + label:after
|
||||
left: calc(100% - 6px)
|
||||
transform: translateX(-70%)
|
||||
|
||||
label
|
||||
cursor: pointer
|
||||
text-indent: -9999px
|
||||
width: 40px
|
||||
height: 20px
|
||||
background: $toggleOffColor
|
||||
display: block
|
||||
border-radius: 100px
|
||||
position: relative
|
||||
transition: background-color 0.5s ease-in-out
|
||||
|
||||
label:after
|
||||
content: ''
|
||||
position: absolute
|
||||
top: 0px
|
||||
left: 0px
|
||||
width: 20px
|
||||
height: 20px
|
||||
background: #ffffff
|
||||
border-radius: 90px
|
||||
transition: all 0.3s ease-in-out
|
||||
|
||||
// label:active:after
|
||||
// width: 20px
|
||||
13
src/components/ToggleSwitch/ToggleSwitch.tsx
Normal file
13
src/components/ToggleSwitch/ToggleSwitch.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import './ToggleSwitch.sass'
|
||||
|
||||
export default (props: { text: string; id: string; onChange?: () => void }) => {
|
||||
return (
|
||||
<section>
|
||||
<div class="toggle">
|
||||
<span class="toggle__text">{props.text}</span>
|
||||
<input class="toggle__switch" type="checkbox" id={props.id} />
|
||||
<label for={props.id}></label>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
45
src/layouts/Layout.astro
Normal file
45
src/layouts/Layout.astro
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
import Navbar from '../components/Navbar/Navbar.tsx'
|
||||
const { title } = Astro.props
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<!-- <meta name="generator" content={Astro.generator} /> -->
|
||||
<meta name="name" content="AIO Tools" />
|
||||
<meta name="description" content="All-in-One Handy Tools for Everyone" />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
|
||||
<body id="body">
|
||||
<Navbar />
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style lang="sass">
|
||||
@use '/src/assets/css/variables.sass' as vars
|
||||
|
||||
@font-face
|
||||
font-family: 'Inter'
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
font-weight: 100 900
|
||||
src: url(@fontsource-variable/inter/files/inter-latin-wght-normal.woff2) format('woff2-variations')
|
||||
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD
|
||||
|
||||
@font-face
|
||||
font-family: 'Montserrat'
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
font-weight: 100 900
|
||||
src: url(@fontsource-variable/montserrat/files/montserrat-latin-wght-normal.woff2) format('woff2-variations')
|
||||
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD
|
||||
|
||||
#body
|
||||
font-family: 'Fira Code Variable', monospace
|
||||
background-color: vars.$background
|
||||
</style>
|
||||
57
src/pages/about.astro
Normal file
57
src/pages/about.astro
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
---
|
||||
|
||||
<Layout title="About - AIO Tools">
|
||||
<main class="page">
|
||||
<h2 class="paragraph-title">About AIO Tools</h2>
|
||||
<p class="paragraph-content">This website will provide you with all the tools you need for your daily tasks. Whether you're working as a developer, a sysadmin, or just someone who needs some handy tools for their daily tasks, this website is here to help.</p>
|
||||
<p class="paragraph-content">
|
||||
Inspired by <a target="_blank" href="https://it-tools.tech/">IT Tools</a> by Corentin Thomasset. This wonderful website, made with ❤ by Patrick Alvin Alcala, aggregates useful tools for developer and people working in IT. If you find it useful, please feel free to share it to people you think may find it useful too and don't forget to bookmark it in your shortcut bar! IT Tools is open-source (under the MIT license) and free, and will always be, but it costs me money to host and renew the domain
|
||||
name. If you want to support my work, and encourage me to add more tools, please consider supporting by sponsoring me.
|
||||
</p>
|
||||
|
||||
<h2 class="paragraph-title">Technologies</h2>
|
||||
<p class="paragraph-content">
|
||||
AIO Tools is made using <a target="_blank" href="https://astro.build/">AstroJS</a> and <a target="_blank" href="https://www.solidjs.com/">SolidJS</a> to achieve maximum performance and to provide a smooth user experience, and is styled with <a target="_blank" href="https://sass-lang.com/">SASS</a>. The UI Components are manually built from scratch for better flexibility. Third-party open-source libraries are used in some tools, you may find the complete list in the package.json file of the
|
||||
repository.
|
||||
</p>
|
||||
|
||||
<h2 class="paragraph-title">Found a bug? A tool is missing?</h2>
|
||||
<p class="paragraph-content">If you need a tool that is currently not present here, and you think can be useful, you are welcome to submit a feature request in the issues section in the GitHub repository. And if you found a bug, or something doesn't work as expected, please file a bug report in the issues section in the GitHub repository.</p>
|
||||
</main>
|
||||
</Layout>
|
||||
|
||||
<style lang="sass">
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
@use '/src/assets/css/variables.sass' as vars
|
||||
@use 'sass:color'
|
||||
|
||||
.page
|
||||
padding: 4rem
|
||||
display: flex
|
||||
flex-direction: column
|
||||
items-align: center
|
||||
color: #ffffff
|
||||
font-family: 'Inter', sans-serif
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
padding: 1rem
|
||||
|
||||
.paragraph-title
|
||||
font-size: 1.8rem
|
||||
font-weight: 500
|
||||
color: color.change(vars.$background, $lightness: 90%)
|
||||
margin-bottom: 1rem
|
||||
|
||||
@media screen and (max-width: view.$mobile)
|
||||
font-size: 1.25rem
|
||||
|
||||
.paragraph-content
|
||||
font-size: 1rem
|
||||
line-height: 1.6
|
||||
color: color.change(vars.$background, $lightness: 80%)
|
||||
|
||||
@media screen and (max-width: view.$mobile)
|
||||
font-size: 0.75rem
|
||||
</style>
|
||||
55
src/pages/hash-generator.astro
Normal file
55
src/pages/hash-generator.astro
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import { FiHash } from 'solid-icons/fi'
|
||||
import HashGeneratorComponent from '../components/Hash-Generator/HashGeneratorComponent.tsx'
|
||||
---
|
||||
|
||||
<Layout title="Hash Generator - AIO Tools">
|
||||
<div class="page">
|
||||
<section class="title">
|
||||
<FiHash class="title__icon" opacity={1} />
|
||||
<h1 class="title__text">Hash Generator</h1>
|
||||
<p class="title__description">Convert text to multiple hash formats for secure data transmission.</p>
|
||||
</section>
|
||||
|
||||
<section class="content">
|
||||
<HashGeneratorComponent client:visible />
|
||||
</section>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style lang="sass">
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
|
||||
.page
|
||||
font-family: 'Inter', sans-serif
|
||||
text-align: center
|
||||
margin: 4rem 0 0 0
|
||||
transition: margin 0.4s ease-in-out
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
margin-top: 1rem
|
||||
|
||||
.title
|
||||
color: #ffffff
|
||||
|
||||
&__text
|
||||
font-size: clamp(1.2rem, 5vw, 2.4rem)
|
||||
margin-bottom: 1rem
|
||||
|
||||
&__description
|
||||
font-size: clamp(0.5rem, 2vw, 1rem)
|
||||
opacity: 0.6
|
||||
margin-bottom: 3rem
|
||||
|
||||
&__icon
|
||||
font-size: clamp(2rem, 5vw, 4rem)
|
||||
|
||||
.content
|
||||
display: flex
|
||||
justify-content: center
|
||||
align-items: center
|
||||
// border: 1px solid #ffffff
|
||||
// padding: 2rem
|
||||
// border-radius: 8px
|
||||
</style>
|
||||
94
src/pages/index.astro
Normal file
94
src/pages/index.astro
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import Card from '../components/Card/Card.jsx'
|
||||
import aioToolsImage from '../assets/images/aio-tools.png'
|
||||
import { Image } from 'astro:assets'
|
||||
---
|
||||
|
||||
<Layout title="AIO Tools">
|
||||
<main class="page">
|
||||
<section class="title--section">
|
||||
<div class="titleandlogo">
|
||||
<Image class="titleandlogo__logo" src={aioToolsImage} alt="AIO Tools" width={90} />
|
||||
<div class="titleandlogo__texts">
|
||||
<span class="titleandlogo__texts__title">AIO Tools</span>
|
||||
<span class="titleandlogo__texts__subtitle">All-in-One Tools for Everyone</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="cards--section">
|
||||
<a href="/password-generator" aria-label="Password generator page"><Card title="Password Generator" description="Generate a strong and secure password for your accounts online." /> </a>
|
||||
<a href="/hash-generator" aria-label="Hash generator page"><Card title="Hash Generator" description="Convert text to multiple hash formats for secure data transmission." /></a>
|
||||
</section>
|
||||
</main>
|
||||
</Layout>
|
||||
|
||||
<style lang="sass">
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
|
||||
.page
|
||||
display: flex
|
||||
flex-direction: column
|
||||
font-family: 'Inter', sans-serif
|
||||
margin-top: 2rem
|
||||
|
||||
.title--section
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: flex-start
|
||||
color: #ffffff
|
||||
padding: 0 2rem
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
.titleandlogo
|
||||
display: flex
|
||||
flex-direction: row
|
||||
gap: 1rem
|
||||
align-items: center
|
||||
font-family: 'Montserrat'
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: center
|
||||
text-align: center
|
||||
|
||||
&__texts
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
&__title
|
||||
font-size: clamp(2rem, 6vw, 3.25rem)
|
||||
font-weight: 500
|
||||
|
||||
&__subtitle
|
||||
font-size: clamp(0.75rem, 3vw, 1.25rem)
|
||||
opacity: 0.8
|
||||
|
||||
&__logo
|
||||
@media screen and (max-width: view.$tablet)
|
||||
width: 80px
|
||||
height: auto
|
||||
|
||||
@media screen and (max-width: view.$mobile)
|
||||
width: 60px
|
||||
|
||||
.cards--section
|
||||
display: flex
|
||||
flex-direction: row
|
||||
// align-items: center
|
||||
flex-wrap: wrap
|
||||
justify-content: center
|
||||
gap: 1rem
|
||||
padding: 0.5rem
|
||||
margin-top: 2rem
|
||||
border-radius: 0.5rem
|
||||
|
||||
a
|
||||
text-decoration: none
|
||||
color: inherit
|
||||
</style>
|
||||
55
src/pages/password-generator.astro
Normal file
55
src/pages/password-generator.astro
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import PasswordGeneratorComponent from '../components/Password-Generator/PasswordGeneratorComponent.tsx'
|
||||
import { RiSystemLockPasswordLine } from 'solid-icons/ri'
|
||||
---
|
||||
|
||||
<Layout title="Password Generator - AIO Tools">
|
||||
<div class="page">
|
||||
<section class="title">
|
||||
<RiSystemLockPasswordLine class="title__icon" opacity={1} />
|
||||
<h1 class="title__text">Password Generator</h1>
|
||||
<p class="title__description">Generate a strong and secure password for your accounts online.</p>
|
||||
</section>
|
||||
|
||||
<section class="content">
|
||||
<PasswordGeneratorComponent client:load />
|
||||
</section>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style lang="sass">
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
|
||||
.page
|
||||
font-family: 'Inter', sans-serif
|
||||
text-align: center
|
||||
margin: 4rem 0 0 0
|
||||
transition: margin 0.4s ease-in-out
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
margin-top: 1rem
|
||||
|
||||
.title
|
||||
color: #ffffff
|
||||
|
||||
&__text
|
||||
font-size: clamp(1.2rem, 5vw, 2.4rem)
|
||||
margin-bottom: 1rem
|
||||
|
||||
&__description
|
||||
font-size: clamp(0.5rem, 2vw, 1rem)
|
||||
opacity: 0.6
|
||||
margin-bottom: 3rem
|
||||
|
||||
&__icon
|
||||
font-size: clamp(2rem, 5vw, 4rem)
|
||||
|
||||
.content
|
||||
display: flex
|
||||
justify-content: center
|
||||
align-items: center
|
||||
// border: 1px solid #ffffff
|
||||
// padding: 2rem
|
||||
// border-radius: 8px
|
||||
</style>
|
||||
52
src/pages/password-manager.astro
Normal file
52
src/pages/password-manager.astro
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import PasswordManagerComponent from '../components/PageComponents/Password-Manager/PasswordManagerComponent.tsx'
|
||||
import { Si1password } from 'solid-icons/si'
|
||||
import PageTitle from '../components/PageTitle/PageTitle.tsx'
|
||||
---
|
||||
|
||||
<Layout title="Password Manager - AIO Tools">
|
||||
<div class="page">
|
||||
<PageTitle title="Password Manager" description="Manage your passwords securely and privately. Recommended to be pair with Password Generator for best security." />
|
||||
|
||||
<section class="content">
|
||||
<PasswordManagerComponent client:visible />
|
||||
</section>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style lang="sass">
|
||||
@use '/src/assets/css/viewport.sass' as view
|
||||
|
||||
.page
|
||||
font-family: 'Inter', sans-serif
|
||||
text-align: center
|
||||
margin: 4rem 0 0 0
|
||||
transition: margin 0.4s ease-in-out
|
||||
|
||||
@media screen and (max-width: view.$tablet)
|
||||
margin-top: 1rem
|
||||
|
||||
.title
|
||||
color: #ffffff
|
||||
|
||||
&__text
|
||||
font-size: clamp(1.2rem, 5vw, 2.4rem)
|
||||
margin-bottom: 1rem
|
||||
|
||||
&__description
|
||||
font-size: clamp(0.5rem, 2vw, 1rem)
|
||||
opacity: 0.6
|
||||
margin-bottom: 3rem
|
||||
|
||||
&__icon
|
||||
font-size: clamp(2rem, 5vw, 4rem)
|
||||
|
||||
.content
|
||||
display: flex
|
||||
justify-content: center
|
||||
align-items: center
|
||||
// border: 1px solid #ffffff
|
||||
// padding: 2rem
|
||||
// border-radius: 8px
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue