refactor(ui): getting ride of naive ui buttons

This commit is contained in:
Corentin Thomasset 2023-04-19 21:38:59 +02:00 committed by Corentin THOMASSET
parent df989e24b3
commit c45bce36f9
44 changed files with 738 additions and 204 deletions

72
components.d.ts vendored
View file

@ -9,13 +9,62 @@ export {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
export interface GlobalComponents { export interface GlobalComponents {
'404.page': typeof import('./src/pages/404.page.vue')['default']
About: typeof import('./src/pages/About.vue')['default']
App: typeof import('./src/App.vue')['default']
'Base.layout': typeof import('./src/layouts/base.layout.vue')['default']
Base64FileConverter: typeof import('./src/tools/base64-file-converter/base64-file-converter.vue')['default']
Base64StringConverter: typeof import('./src/tools/base64-string-converter/base64-string-converter.vue')['default']
BasicAuthGenerator: typeof import('./src/tools/basic-auth-generator/basic-auth-generator.vue')['default']
Bcrypt: typeof import('./src/tools/bcrypt/bcrypt.vue')['default']
BenchmarkBuilder: typeof import('./src/tools/benchmark-builder/benchmark-builder.vue')['default']
Bip39Generator: typeof import('./src/tools/bip39-generator/bip39-generator.vue')['default']
CaseConverter: typeof import('./src/tools/case-converter/case-converter.vue')['default']
CButton: typeof import('./src/ui/c-button/c-button.vue')['default']
ChmodCalculator: typeof import('./src/tools/chmod-calculator/chmod-calculator.vue')['default']
Chronometer: typeof import('./src/tools/chronometer/chronometer.vue')['default']
CLink: typeof import('./src/ui/c-link/c-link.vue')['default']
CollapsibleToolMenu: typeof import('./src/components/CollapsibleToolMenu.vue')['default'] CollapsibleToolMenu: typeof import('./src/components/CollapsibleToolMenu.vue')['default']
ColorConverter: typeof import('./src/tools/color-converter/color-converter.vue')['default']
ColoredCard: typeof import('./src/components/ColoredCard.vue')['default'] ColoredCard: typeof import('./src/components/ColoredCard.vue')['default']
CopyableIpLike: typeof import('./src/tools/ipv4-subnet-calculator/copyable-ip-like.vue')['default']
CrontabGenerator: typeof import('./src/tools/crontab-generator/crontab-generator.vue')['default']
DateTimeConverter: typeof import('./src/tools/date-time-converter/date-time-converter.vue')['default']
DeviceInformation: typeof import('./src/tools/device-information/device-information.vue')['default']
DockerRunToDockerComposeConverter: typeof import('./src/tools/docker-run-to-docker-compose-converter/docker-run-to-docker-compose-converter.vue')['default']
DynamicValues: typeof import('./src/tools/benchmark-builder/dynamic-values.vue')['default']
Editor: typeof import('./src/tools/html-wysiwyg-editor/editor/editor.vue')['default']
Encryption: typeof import('./src/tools/encryption/encryption.vue')['default']
EtaCalculator: typeof import('./src/tools/eta-calculator/eta-calculator.vue')['default']
FavoriteButton: typeof import('./src/components/FavoriteButton.vue')['default'] FavoriteButton: typeof import('./src/components/FavoriteButton.vue')['default']
FormatTransformer: typeof import('./src/components/FormatTransformer.vue')['default'] FormatTransformer: typeof import('./src/components/FormatTransformer.vue')['default']
GitMemo: typeof import('./src/tools/git-memo/git-memo.md')['default']
HashText: typeof import('./src/tools/hash-text/hash-text.vue')['default']
HmacGenerator: typeof import('./src/tools/hmac-generator/hmac-generator.vue')['default']
'Home.page': typeof import('./src/pages/Home.page.vue')['default']
HtmlEntities: typeof import('./src/tools/html-entities/html-entities.vue')['default']
HtmlWysiwygEditor: typeof import('./src/tools/html-wysiwyg-editor/html-wysiwyg-editor.vue')['default']
HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default']
InputCopyable: typeof import('./src/components/InputCopyable.vue')['default'] InputCopyable: typeof import('./src/components/InputCopyable.vue')['default']
IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default']
Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default']
Ipv4RangeExpander: typeof import('./src/tools/ipv4-range-expander/ipv4-range-expander.vue')['default']
Ipv4SubnetCalculator: typeof import('./src/tools/ipv4-subnet-calculator/ipv4-subnet-calculator.vue')['default']
Ipv6UlaGenerator: typeof import('./src/tools/ipv6-ula-generator/ipv6-ula-generator.vue')['default']
JsonMinify: typeof import('./src/tools/json-minify/json-minify.vue')['default']
JsonToYaml: typeof import('./src/tools/json-to-yaml-converter/json-to-yaml.vue')['default']
JsonViewer: typeof import('./src/tools/json-viewer/json-viewer.vue')['default']
JwtParser: typeof import('./src/tools/jwt-parser/jwt-parser.vue')['default']
KeycodeInfo: typeof import('./src/tools/keycode-info/keycode-info.vue')['default']
LoremIpsumGenerator: typeof import('./src/tools/lorem-ipsum-generator/lorem-ipsum-generator.vue')['default']
MacAddressLookup: typeof import('./src/tools/mac-address-lookup/mac-address-lookup.vue')['default']
MathEvaluator: typeof import('./src/tools/math-evaluator/math-evaluator.vue')['default']
MenuBar: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar.vue')['default']
MenuBarItem: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue')['default']
MenuIconItem: typeof import('./src/components/MenuIconItem.vue')['default'] MenuIconItem: typeof import('./src/components/MenuIconItem.vue')['default']
MenuLayout: typeof import('./src/components/MenuLayout.vue')['default'] MenuLayout: typeof import('./src/components/MenuLayout.vue')['default']
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
NAlert: typeof import('naive-ui')['NAlert'] NAlert: typeof import('naive-ui')['NAlert']
NAutoComplete: typeof import('naive-ui')['NAutoComplete'] NAutoComplete: typeof import('naive-ui')['NAutoComplete']
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default'] NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
@ -37,7 +86,6 @@ declare module '@vue/runtime-core' {
NH1: typeof import('naive-ui')['NH1'] NH1: typeof import('naive-ui')['NH1']
NH2: typeof import('naive-ui')['NH2'] NH2: typeof import('naive-ui')['NH2']
NH3: typeof import('naive-ui')['NH3'] NH3: typeof import('naive-ui')['NH3']
NH4: typeof import('naive-ui')['NH4']
NIcon: typeof import('naive-ui')['NIcon'] NIcon: typeof import('naive-ui')['NIcon']
NImage: typeof import('naive-ui')['NImage'] NImage: typeof import('naive-ui')['NImage']
NInput: typeof import('naive-ui')['NInput'] NInput: typeof import('naive-ui')['NInput']
@ -50,7 +98,6 @@ declare module '@vue/runtime-core' {
NP: typeof import('naive-ui')['NP'] NP: typeof import('naive-ui')['NP']
NPageHeader: typeof import('naive-ui')['NPageHeader'] NPageHeader: typeof import('naive-ui')['NPageHeader']
NProgress: typeof import('naive-ui')['NProgress'] NProgress: typeof import('naive-ui')['NProgress']
NResult: typeof import('naive-ui')['NResult']
NScrollbar: typeof import('naive-ui')['NScrollbar'] NScrollbar: typeof import('naive-ui')['NScrollbar']
NSelect: typeof import('naive-ui')['NSelect'] NSelect: typeof import('naive-ui')['NSelect']
NSlider: typeof import('naive-ui')['NSlider'] NSlider: typeof import('naive-ui')['NSlider']
@ -63,12 +110,33 @@ declare module '@vue/runtime-core' {
NTooltip: typeof import('naive-ui')['NTooltip'] NTooltip: typeof import('naive-ui')['NTooltip']
NUpload: typeof import('naive-ui')['NUpload'] NUpload: typeof import('naive-ui')['NUpload']
NUploadDragger: typeof import('naive-ui')['NUploadDragger'] NUploadDragger: typeof import('naive-ui')['NUploadDragger']
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
QrCodeGenerator: typeof import('./src/tools/qr-code-generator/qr-code-generator.vue')['default']
RandomPortGenerator: typeof import('./src/tools/random-port-generator/random-port-generator.vue')['default']
ResultRow: typeof import('./src/tools/ipv4-range-expander/result-row.vue')['default']
RomanNumeralConverter: typeof import('./src/tools/roman-numeral-converter/roman-numeral-converter.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
SearchBar: typeof import('./src/components/SearchBar.vue')['default'] SearchBar: typeof import('./src/components/SearchBar.vue')['default']
SearchBarItem: typeof import('./src/components/SearchBarItem.vue')['default'] SearchBarItem: typeof import('./src/components/SearchBarItem.vue')['default']
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default'] SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']
SvgPlaceholderGenerator: typeof import('./src/tools/svg-placeholder-generator/svg-placeholder-generator.vue')['default']
TemperatureConverter: typeof import('./src/tools/temperature-converter/temperature-converter.vue')['default']
TextareaCopyable: typeof import('./src/components/TextareaCopyable.vue')['default'] TextareaCopyable: typeof import('./src/components/TextareaCopyable.vue')['default']
TextStatistics: typeof import('./src/tools/text-statistics/text-statistics.vue')['default']
TextToNatoAlphabet: typeof import('./src/tools/text-to-nato-alphabet/text-to-nato-alphabet.vue')['default']
TokenDisplay: typeof import('./src/tools/otp-code-generator-and-validator/token-display.vue')['default']
'TokenGenerator.tool': typeof import('./src/tools/token-generator/token-generator.tool.vue')['default']
'Tool.layout': typeof import('./src/layouts/tool.layout.vue')['default']
ToolCard: typeof import('./src/components/ToolCard.vue')['default'] ToolCard: typeof import('./src/components/ToolCard.vue')['default']
UrlEncoder: typeof import('./src/tools/url-encoder/url-encoder.vue')['default']
UrlParser: typeof import('./src/tools/url-parser/url-parser.vue')['default']
UserAgentParser: typeof import('./src/tools/user-agent-parser/user-agent-parser.vue')['default']
UserAgentResultCards: typeof import('./src/tools/user-agent-parser/user-agent-result-cards.vue')['default']
UuidGenerator: typeof import('./src/tools/uuid-generator/uuid-generator.vue')['default']
YamlToJson: typeof import('./src/tools/yaml-to-json-converter/yaml-to-json.vue')['default']
} }
} }

View file

@ -1,11 +1,15 @@
<template> <template>
<n-tooltip trigger="hover"> <n-tooltip trigger="hover">
<template #trigger> <template #trigger>
<n-button circle quaternary :type="buttonType" :style="{ opacity: isFavorite ? 1 : 0.2 }" @click="toggleFavorite"> <c-button
<template #icon> variant="text"
<n-icon :component="FavoriteFilled" /> circle
</template> :type="buttonType"
</n-button> :style="{ opacity: isFavorite ? 1 : 0.2 }"
@click="toggleFavorite"
>
<n-icon :component="FavoriteFilled" />
</c-button>
</template> </template>
{{ isFavorite ? 'Remove from favorites' : 'Add to favorites' }} {{ isFavorite ? 'Remove from favorites' : 'Add to favorites' }}
</n-tooltip> </n-tooltip>

View file

@ -3,9 +3,9 @@
<template #suffix> <template #suffix>
<n-tooltip trigger="hover"> <n-tooltip trigger="hover">
<template #trigger> <template #trigger>
<n-button quaternary circle @click="onCopyClicked"> <c-button circle variant="text" @click="onCopyClicked">
<n-icon :component="ContentCopyFilled" /> <n-icon :component="ContentCopyFilled" />
</n-button> </c-button>
</template> </template>
{{ tooltipText }} {{ tooltipText }}
</n-tooltip> </n-tooltip>

View file

@ -1,56 +1,50 @@
<template> <template>
<n-tooltip trigger="hover"> <n-tooltip trigger="hover">
<template #trigger> <template #trigger>
<n-button <c-button
size="large"
circle circle
quaternary variant="text"
tag="a"
href="https://github.com/CorentinTh/it-tools" href="https://github.com/CorentinTh/it-tools"
rel="noopener"
target="_blank" target="_blank"
rel="noopener noreferrer"
aria-label="IT-Tools' GitHub repository" aria-label="IT-Tools' GitHub repository"
> >
<n-icon size="25" :component="BrandGithub" /> <n-icon size="25" :component="BrandGithub" />
</n-button> </c-button>
</template> </template>
Github repository Github repository
</n-tooltip> </n-tooltip>
<n-tooltip trigger="hover"> <n-tooltip trigger="hover">
<template #trigger> <template #trigger>
<n-button <c-button
size="large"
circle circle
quaternary variant="text"
tag="a"
href="https://twitter.com/ittoolsdottech" href="https://twitter.com/ittoolsdottech"
rel="noopener" rel="noopener"
target="_blank" target="_blank"
aria-label="IT Tools' Twitter account" aria-label="IT Tools' Twitter account"
> >
<n-icon size="25" :component="BrandTwitter" /> <n-icon size="25" :component="BrandTwitter" />
</n-button> </c-button>
</template> </template>
IT Tools' Twitter account IT Tools' Twitter account
</n-tooltip> </n-tooltip>
<router-link to="/about" #="{ navigate, href }" custom>
<n-tooltip trigger="hover">
<template #trigger>
<n-button tag="a" :href="href" circle quaternary size="large" aria-label="About" @click="navigate">
<n-icon size="25" :component="InfoCircle" />
</n-button>
</template>
About
</n-tooltip>
</router-link>
<n-tooltip trigger="hover"> <n-tooltip trigger="hover">
<template #trigger> <template #trigger>
<n-button size="large" circle quaternary aria-label="Toggle dark/light mode" @click="isDarkTheme = !isDarkTheme"> <c-button circle variant="text" to="/about" aria-label="About">
<n-icon size="25" :component="InfoCircle" />
</c-button>
</template>
About
</n-tooltip>
<n-tooltip trigger="hover">
<template #trigger>
<c-button circle variant="text" aria-label="Toggle dark/light mode" @click="toggleDarkTheme">
<n-icon v-if="isDarkTheme" size="25" :component="Sun" /> <n-icon v-if="isDarkTheme" size="25" :component="Sun" />
<n-icon v-else size="25" :component="Moon" /> <n-icon v-else size="25" :component="Moon" />
</n-button> </c-button>
</template> </template>
<span v-if="isDarkTheme">Light mode</span> <span v-if="isDarkTheme">Light mode</span>
<span v-else>Dark mode</span> <span v-else>Dark mode</span>
@ -59,11 +53,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { useStyleStore } from '@/stores/style.store'; import { useStyleStore } from '@/stores/style.store';
import { useThemeStore } from '@/ui/theme/theme.store';
import { BrandGithub, BrandTwitter, InfoCircle, Moon, Sun } from '@vicons/tabler'; import { BrandGithub, BrandTwitter, InfoCircle, Moon, Sun } from '@vicons/tabler';
import { toRefs } from 'vue'; import { toRefs } from 'vue';
const styleStore = useStyleStore(); const styleStore = useStyleStore();
const { isDarkTheme } = toRefs(styleStore); const { isDarkTheme } = toRefs(styleStore);
const themeStore = useThemeStore();
function toggleDarkTheme() {
isDarkTheme.value = !isDarkTheme.value;
themeStore.toggleTheme();
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View file

@ -13,16 +13,16 @@
<n-tooltip v-if="value" trigger="hover"> <n-tooltip v-if="value" trigger="hover">
<template #trigger> <template #trigger>
<div class="copy-button" :class="[copyPlacement]"> <div class="copy-button" :class="[copyPlacement]">
<n-button circle secondary size="large" @click="onCopyClicked"> <c-button circle important:h-10 important:w-10 @click="onCopyClicked">
<n-icon size="22" :component="Copy" /> <n-icon size="22" :component="Copy" />
</n-button> </c-button>
</div> </div>
</template> </template>
<span>{{ tooltipText }}</span> <span>{{ tooltipText }}</span>
</n-tooltip> </n-tooltip>
</n-card> </n-card>
<n-space v-if="copyPlacement === 'outside'" justify="center" mt-4> <n-space v-if="copyPlacement === 'outside'" justify="center" mt-4>
<n-button secondary @click="onCopyClicked"> {{ tooltipText }} </n-button> <c-button @click="onCopyClicked"> {{ tooltipText }} </c-button>
</n-space> </n-space>
</div> </div>
</template> </template>

View file

@ -53,38 +53,25 @@ const tools = computed<ToolCategory[]>(() => [
<div> <div>
IT-Tools IT-Tools
<n-button <c-link target="_blank" rel="noopener" :href="`https://github.com/CorentinTh/it-tools/tree/v${version}`">
text
tag="a"
target="_blank"
rel="noopener"
type="primary"
depth="3"
:href="`https://github.com/CorentinTh/it-tools/tree/v${version}`"
>
v{{ version }} v{{ version }}
</n-button> </c-link>
<template v-if="commitSha && commitSha.length > 0"> <template v-if="commitSha && commitSha.length > 0">
- -
<n-button <c-link
text
tag="a"
target="_blank" target="_blank"
rel="noopener" rel="noopener"
type="primary" type="primary"
depth="3"
:href="`https://github.com/CorentinTh/it-tools/tree/${commitSha}`" :href="`https://github.com/CorentinTh/it-tools/tree/${commitSha}`"
> >
{{ commitSha }} {{ commitSha }}
</n-button> </c-link>
</template> </template>
</div> </div>
<div> <div>
© {{ new Date().getFullYear() }} © {{ new Date().getFullYear() }}
<n-button text tag="a" target="_blank" rel="noopener" type="primary" href="https://github.com/CorentinTh"> <c-link target="_blank" rel="noopener" href="https://github.com/CorentinTh"> Corentin Thomasset </c-link>
Corentin Thomasset
</n-button>
</div> </div>
</div> </div>
</div> </div>
@ -92,34 +79,24 @@ const tools = computed<ToolCategory[]>(() => [
<template #content> <template #content>
<div class="navigation"> <div class="navigation">
<n-button <c-button
:size="styleStore.isSmallScreen ? 'medium' : 'large'" :size="styleStore.isSmallScreen ? 'medium' : 'large'"
circle circle
quaternary variant="text"
aria-label="Toggle menu" aria-label="Toggle menu"
@click="styleStore.isMenuCollapsed = !styleStore.isMenuCollapsed" @click="styleStore.isMenuCollapsed = !styleStore.isMenuCollapsed"
> >
<n-icon size="25" :component="Menu2" /> <n-icon size="25" :component="Menu2" />
</n-button> </c-button>
<router-link to="/" #="{ navigate, href }" custom> <n-tooltip trigger="hover">
<n-tooltip trigger="hover"> <template #trigger>
<template #trigger> <c-button to="/" circle variant="text" aria-label="Home">
<n-button <n-icon size="25" :component="Home2" />
tag="a" </c-button>
:href="href" </template>
:size="styleStore.isSmallScreen ? 'medium' : 'large'" Home
circle </n-tooltip>
quaternary
aria-label="Home"
@click="navigate"
>
<n-icon size="25" :component="Home2" />
</n-button>
</template>
Home
</n-tooltip>
</router-link>
<search-bar /> <search-bar />
@ -127,10 +104,8 @@ const tools = computed<ToolCategory[]>(() => [
<n-tooltip trigger="hover"> <n-tooltip trigger="hover">
<template #trigger> <template #trigger>
<n-button <c-button
round round
type="primary"
tag="a"
href="https://www.buymeacoffee.com/cthmsst" href="https://www.buymeacoffee.com/cthmsst"
rel="noopener" rel="noopener"
target="_blank" target="_blank"
@ -140,7 +115,7 @@ const tools = computed<ToolCategory[]>(() => [
> >
Buy me a coffee Buy me a coffee
<n-icon v-if="!styleStore.isSmallScreen" :component="Heart" ml-2 /> <n-icon v-if="!styleStore.isSmallScreen" :component="Heart" ml-2 />
</n-button> </c-button>
</template> </template>
Support IT Tools development ! Support IT Tools development !
</n-tooltip> </n-tooltip>
@ -165,8 +140,8 @@ const tools = computed<ToolCategory[]>(() => [
.support-button { .support-button {
background: rgb(37, 99, 108); background: rgb(37, 99, 108);
background: linear-gradient(48deg, rgba(37, 99, 108, 1) 0%, rgba(59, 149, 111, 1) 60%, rgba(20, 160, 88, 1) 100%); background: linear-gradient(48deg, rgba(37, 99, 108, 1) 0%, rgba(59, 149, 111, 1) 60%, rgba(20, 160, 88, 1) 100%);
color: #fff; color: #fff !important;
transition: all ease 0.2s; transition: padding ease 0.2s !important;
&:hover { &:hover {
color: #fff; color: #fff;

View file

@ -13,8 +13,6 @@ useHead({ title: 'Page not found - IT Tools' });
<n-text mt-4 block depth="3">Sorry, this page does not seem to exist</n-text> <n-text mt-4 block depth="3">Sorry, this page does not seem to exist</n-text>
<n-text mb-8 block depth="3">Maybe the cache is doing tricky things, try force-refreshing?</n-text> <n-text mb-8 block depth="3">Maybe the cache is doing tricky things, try force-refreshing?</n-text>
<router-link to="/" #="{ navigate, href }" custom> <c-button to="/"> Back home </c-button>
<n-button tag="a" :href="href" secondary @click="navigate"> Back home </n-button>
</router-link>
</div> </div>
</template> </template>

View file

@ -11,25 +11,21 @@ const { tracker } = useTracker();
<n-h1>About</n-h1> <n-h1>About</n-h1>
<n-p> <n-p>
This wonderful website, made with by This wonderful website, made with by
<n-button text tag="a" href="https://github.com/CorentinTh" target="_blank" rel="noopener" type="primary"> <c-link href="https://github.com/CorentinTh" target="_blank" rel="noopener"> Corentin Thomasset </c-link>,
Corentin Thomasset </n-button aggregates useful tools for developer and people working in IT. If you find it useful, please fell free to share
>, aggregates useful tools for developer and people working in IT. If you find it useful, please fell free to it to people you think may find it useful too and don't forget to pin it in your shortcut bar !
share it to people you think may find it useful too and don't forget to pin it in your shortcut bar !
</n-p> </n-p>
<n-p> <n-p>
IT Tools is open-source (under the MIT license) and free, and will always be, but it cost me money to host and IT Tools is open-source (under the MIT license) and free, and will always be, but it cost 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 renew the domain name, if you want to support my work, and encourage me to add more tools, please consider
supporting by supporting by
<n-button <c-link
type="primary"
tag="a"
text
href="https://www.buymeacoffee.com/cthmsst" href="https://www.buymeacoffee.com/cthmsst"
rel="noopener" rel="noopener"
target="_blank" target="_blank"
@click="() => tracker.trackEvent({ eventName: 'Support button clicked' })" @click="() => tracker.trackEvent({ eventName: 'Support button clicked' })"
> >
sponsoring me </n-button sponsoring me </c-link
>. >.
</n-p> </n-p>
@ -37,16 +33,9 @@ const { tracker } = useTracker();
<n-p> <n-p>
IT Tools is made in Vue JS (vue 3) with the the naive-ui component library and is hosted and continuously deployed IT Tools is made in Vue JS (vue 3) with the the naive-ui component library and is hosted and continuously deployed
by Vercel. Third party open-source libraries are used in some tools, you may find the complete list in the by Vercel. Third party open-source libraries are used in some tools, you may find the complete list in the
<n-button <c-link href="https://github.com/CorentinTh/it-tools/blob/main/package.json" rel="noopener" target="_blank">
type="primary"
tag="a"
text
href="https://github.com/CorentinTh/it-tools/blob/main/package.json"
rel="noopener"
target="_blank"
>
package.json package.json
</n-button> </c-link>
file of the repository. file of the repository.
</n-p> </n-p>
@ -54,30 +43,24 @@ const { tracker } = useTracker();
<n-p> <n-p>
If you need a tool that is currently not present here, and you think can be relevant, you are welcome to submit a If you need a tool that is currently not present here, and you think can be relevant, you are welcome to submit a
feature request in the feature request in the
<n-button <c-link
type="primary"
tag="a"
text
href="https://github.com/CorentinTh/it-tools/issues/new?assignees=CorentinTh&labels=enhancement&template=feature_request.md&title=%5BFEAT%5D%20My%20feature" href="https://github.com/CorentinTh/it-tools/issues/new?assignees=CorentinTh&labels=enhancement&template=feature_request.md&title=%5BFEAT%5D%20My%20feature"
rel="noopener" rel="noopener"
target="_blank" target="_blank"
> >
issues section issues section
</n-button> </c-link>
in the GitHub repository. in the GitHub repository.
</n-p> </n-p>
<n-p> <n-p>
And if you found a bug, or something broken that doesn't work as expected, please fill a bug report in the And if you found a bug, or something broken that doesn't work as expected, please fill a bug report in the
<n-button <c-link
type="primary"
tag="a"
text
href="https://github.com/CorentinTh/it-tools/issues/new?assignees=CorentinTh&labels=bug&template=bug_report.md&title=%5BBUG%5D%20My%20bug" href="https://github.com/CorentinTh/it-tools/issues/new?assignees=CorentinTh&labels=bug&template=bug_report.md&title=%5BBUG%5D%20My%20bug"
rel="noopener" rel="noopener"
target="_blank" target="_blank"
> >
issues section issues section
</n-button> </c-link>
in the GitHub repository. in the GitHub repository.
</n-p> </n-p>
</div> </div>

View file

@ -8,9 +8,9 @@
<n-input v-model:value="base64Input" type="textarea" placeholder="Put your base64 file string here..." rows="5" /> <n-input v-model:value="base64Input" type="textarea" placeholder="Put your base64 file string here..." rows="5" />
</n-form-item> </n-form-item>
<n-space justify="center"> <n-space justify="center">
<n-button :disabled="base64Input === '' || !base64InputValidation.isValid" secondary @click="downloadFile()"> <c-button :disabled="base64Input === '' || !base64InputValidation.isValid" @click="downloadFile()">
Download file Download file
</n-button> </c-button>
</n-space> </n-space>
</n-card> </n-card>
@ -26,7 +26,7 @@
<n-input :value="fileBase64" type="textarea" readonly placeholder="File in base64 will be here" /> <n-input :value="fileBase64" type="textarea" readonly placeholder="File in base64 will be here" />
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copyFileBase64()"> Copy </n-button> <c-button @click="copyFileBase64()"> Copy </c-button>
</n-space> </n-space>
</n-card> </n-card>
</template> </template>

View file

@ -15,7 +15,7 @@
</n-form-item> </n-form-item>
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copyTextBase64()"> Copy base64 </n-button> <c-button @click="copyTextBase64()"> Copy base64 </c-button>
</n-space> </n-space>
</n-card> </n-card>
@ -29,7 +29,7 @@
</n-form-item> </n-form-item>
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copyText()"> Copy decoded string </n-button> <c-button @click="copyText()"> Copy decoded string </c-button>
</n-space> </n-space>
</n-card> </n-card>
</template> </template>

View file

@ -23,7 +23,7 @@
</n-card> </n-card>
<br /> <br />
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copy">Copy header</n-button> <c-button @click="copy">Copy header</c-button>
</n-space> </n-space>
</div> </div>
</template> </template>

View file

@ -18,7 +18,7 @@
</n-form> </n-form>
<br /> <br />
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copy"> Copy hash </n-button> <c-button @click="copy"> Copy hash </c-button>
</n-space> </n-space>
</n-card> </n-card>

View file

@ -14,18 +14,17 @@
</n-card> </n-card>
<n-space justify="center"> <n-space justify="center">
<n-button v-if="suites.length > 1" quaternary @click="suites.splice(index, 1)"> <c-button v-if="suites.length > 1" variant="text" @click="suites.splice(index, 1)">
<template #icon> <n-icon :component="Trash" depth="3" mr-2 size="18" />
<n-icon :component="Trash" depth="3" />
</template>
Delete suite Delete suite
</n-button> </c-button>
<n-button quaternary @click="suites.splice(index + 1, 0, { data: [0], title: `Suite ${suites.length + 1}` })"> <c-button
<template #icon> variant="text"
<n-icon :component="Plus" depth="3" /> @click="suites.splice(index + 1, 0, { data: [0], title: `Suite ${suites.length + 1}` })"
</template> >
<n-icon :component="Plus" depth="3" mr-2 size="18" />
Add suite Add suite
</n-button> </c-button>
</n-space> </n-space>
</div> </div>
</n-space> </n-space>
@ -39,15 +38,14 @@
<n-input v-model:value="unit" placeholder="Unit (eg: ms)" /> <n-input v-model:value="unit" placeholder="Unit (eg: ms)" />
</n-form-item> </n-form-item>
<n-button <c-button
tertiary
@click=" @click="
suites = [ suites = [
{ title: 'Suite 1', data: [] }, { title: 'Suite 1', data: [] },
{ title: 'Suite 2', data: [] }, { title: 'Suite 2', data: [] },
] ]
" "
>Reset suites</n-button >Reset suites</c-button
> >
</n-space> </n-space>
@ -73,8 +71,8 @@
</n-table> </n-table>
<br /> <br />
<n-space justify="center"> <n-space justify="center">
<n-button tertiary @click="copyAsMarkdown">Copy as markdown table</n-button> <c-button @click="copyAsMarkdown">Copy as markdown table</c-button>
<n-button tertiary @click="copyAsBulletList">Copy as bullet list</n-button> <c-button @click="copyAsBulletList">Copy as bullet list</c-button>
</n-space> </n-space>
</div> </div>
</div> </div>

View file

@ -11,22 +11,18 @@
/> />
<n-tooltip> <n-tooltip>
<template #trigger> <template #trigger>
<n-button circle quaternary @click="values.splice(index, 1)"> <c-button circle variant="text" @click="values.splice(index, 1)">
<template #icon> <n-icon :component="Trash" depth="3" size="18" />
<n-icon :component="Trash" depth="3" /> </c-button>
</template>
</n-button>
</template> </template>
Delete value Delete value
</n-tooltip> </n-tooltip>
</n-space> </n-space>
<n-button tertiary @click="addValue"> <c-button @click="addValue">
<template #icon> <n-icon :component="Plus" depth="3" mr-2 size="18" />
<n-icon :component="Plus" />
</template>
Add a measure Add a measure
</n-button> </c-button>
</div> </div>
</template> </template>

View file

@ -18,16 +18,16 @@
> >
<n-input-group> <n-input-group>
<n-input v-model:value="entropy" placeholder="Your string..." /> <n-input v-model:value="entropy" placeholder="Your string..." />
<n-button @click="refreshEntropy"> <c-button @click="refreshEntropy">
<n-icon size="22"> <n-icon size="22">
<Refresh /> <Refresh />
</n-icon> </n-icon>
</n-button> </c-button>
<n-button @click="copyEntropy"> <c-button @click="copyEntropy">
<n-icon size="22"> <n-icon size="22">
<Copy /> <Copy />
</n-icon> </n-icon>
</n-button> </c-button>
</n-input-group> </n-input-group>
</n-form-item> </n-form-item>
</n-gi> </n-gi>
@ -48,9 +48,9 @@
spellcheck="false" spellcheck="false"
/> />
<n-button @click="copyPassphrase"> <c-button @click="copyPassphrase">
<n-icon size="22" :component="Copy" /> <n-icon size="22" :component="Copy" />
</n-button> </c-button>
</n-input-group> </n-input-group>
</n-form-item> </n-form-item>
</n-card> </n-card>

View file

@ -5,10 +5,10 @@
</n-card> </n-card>
<br /> <br />
<n-space justify="center"> <n-space justify="center">
<n-button v-if="!isRunning" secondary type="primary" @click="resume">Start</n-button> <c-button v-if="!isRunning" secondary type="primary" @click="resume">Start</c-button>
<n-button v-else secondary type="warning" @click="pause">Stop</n-button> <c-button v-else secondary type="warning" @click="pause">Stop</c-button>
<n-button secondary @click="counter = 0">Reset</n-button> <c-button @click="counter = 0">Reset</c-button>
</n-space> </n-space>
</div> </div>
</template> </template>

View file

@ -16,7 +16,7 @@
<br /> <br />
<br /> <br />
<n-space justify="center"> <n-space justify="center">
<n-button :disabled="dockerCompose === ''" secondary @click="download"> Download docker-compose.yml </n-button> <c-button :disabled="dockerCompose === ''" secondary @click="download"> Download docker-compose.yml </c-button>
</n-space> </n-space>
<div v-if="notComposable.length > 0"> <div v-if="notComposable.length > 0">

View file

@ -43,7 +43,7 @@
<n-input readonly :value="hmac" type="textarea" placeholder="The result of the HMAC..." /> <n-input readonly :value="hmac" type="textarea" placeholder="The result of the HMAC..." />
</n-form-item> </n-form-item>
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copy()">Copy HMAC</n-button> <c-button @click="copy()">Copy HMAC</c-button>
</n-space> </n-space>
</div> </div>
</template> </template>

View file

@ -20,7 +20,7 @@
</n-form-item> </n-form-item>
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copyEscaped"> Copy </n-button> <c-button @click="copyEscaped"> Copy </c-button>
</n-space> </n-space>
</n-card> </n-card>
<n-card title="Unescape html entities"> <n-card title="Unescape html entities">
@ -44,7 +44,7 @@
</n-form-item> </n-form-item>
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copyUnescaped"> Copy </n-button> <c-button @click="copyUnescaped"> Copy </c-button>
</n-space> </n-space>
</n-card> </n-card>
</template> </template>

View file

@ -1,11 +1,9 @@
<template> <template>
<n-tooltip trigger="hover"> <n-tooltip trigger="hover">
<template #trigger> <template #trigger>
<n-button circle quaternary :type="isActive?.() ? 'primary' : 'default'" @click="action"> <c-button circle variant="text" :type="isActive?.() ? 'primary' : 'default'" @click="action">
<template #icon> <n-icon :component="icon" />
<n-icon :component="icon" /> </c-button>
</template>
</n-button>
</template> </template>
{{ title }} {{ title }}

View file

@ -39,10 +39,10 @@
The end IPv4 address is lower than the start IPv4 address. This is not valid and no result could be The end IPv4 address is lower than the start IPv4 address. This is not valid and no result could be
calculated. In the most cases the solution to solve this problem is to change start and end address. calculated. In the most cases the solution to solve this problem is to change start and end address.
</n-text> </n-text>
<n-button quaternary @click="onSwitchStartEndClicked"> <c-button @click="onSwitchStartEndClicked">
<n-icon :component="ChangeCircleOutlined" /> <n-icon mr-2 :component="Exchange" depth="3" size="22" />
&nbsp;&nbsp;Switch start and end IPv4 address Switch start and end IPv4 address
</n-button> </c-button>
</n-space> </n-space>
</n-alert> </n-alert>
</div> </div>
@ -52,7 +52,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useValidation } from '@/composable/validation'; import { useValidation } from '@/composable/validation';
import { ChangeCircleOutlined } from '@vicons/material'; import { Exchange } from '@vicons/tabler';
import { isValidIpv4 } from '../ipv4-address-converter/ipv4-address-converter.service'; import { isValidIpv4 } from '../ipv4-address-converter/ipv4-address-converter.service';
import type { Ipv4RangeExpanderResult } from './ipv4-range-expander.types'; import type { Ipv4RangeExpanderResult } from './ipv4-range-expander.types';
import { calculateCidr } from './ipv4-range-expander.service'; import { calculateCidr } from './ipv4-range-expander.service';

View file

@ -20,14 +20,14 @@
</n-table> </n-table>
<n-space style="margin-top: 14px" justify="space-between"> <n-space style="margin-top: 14px" justify="space-between">
<n-button tertiary @click="switchToBlock({ count: -1 })"> <c-button @click="switchToBlock({ count: -1 })">
<n-icon :component="ArrowLeft" /> <n-icon :component="ArrowLeft" />
Previous block Previous block
</n-button> </c-button>
<n-button tertiary @click="switchToBlock({ count: 1 })"> <c-button @click="switchToBlock({ count: 1 })">
Next block Next block
<n-icon :component="ArrowRight" /> <n-icon :component="ArrowRight" />
</n-button> </c-button>
</n-space> </n-space>
</div> </div>
</div> </div>

View file

@ -22,7 +22,7 @@
<br /> <br />
<br /> <br />
<n-space justify="center"> <n-space justify="center">
<n-button secondary autofocus @click="copy"> Copy </n-button> <c-button autofocus @click="copy"> Copy </c-button>
</n-space> </n-space>
</n-card> </n-card>
</template> </template>

View file

@ -24,7 +24,7 @@
</n-form-item> </n-form-item>
<n-space justify="center"> <n-space justify="center">
<n-button :disabled="!details" tertiary> Copy vendor info </n-button> <c-button :disabled="!details"> Copy vendor info </c-button>
</n-space> </n-space>
</div> </div>
</template> </template>

View file

@ -5,9 +5,9 @@
<template #suffix> <template #suffix>
<n-tooltip trigger="hover"> <n-tooltip trigger="hover">
<template #trigger> <template #trigger>
<n-button quaternary circle @click="refreshSecret"> <c-button circle variant="text" @click="refreshSecret">
<n-icon :component="Refresh" /> <n-icon :component="Refresh" />
</n-button> </c-button>
</template> </template>
Generate secret token Generate secret token
</n-tooltip> </n-tooltip>
@ -23,7 +23,7 @@
</div> </div>
<n-space justify="center" vertical align="center" style="margin-top: 10px"> <n-space justify="center" vertical align="center" style="margin-top: 10px">
<n-image :src="qrcode"></n-image> <n-image :src="qrcode"></n-image>
<n-button secondary tag="a" :href="keyUri" target="_blank">Open Key URI in new tab</n-button> <c-button :href="keyUri" target="_blank">Open Key URI in new tab</c-button>
</n-space> </n-space>
</div> </div>
<div style="max-width: 350px"> <div style="max-width: 350px">

View file

@ -8,31 +8,30 @@
<n-input-group> <n-input-group>
<n-tooltip trigger="hover" placement="bottom"> <n-tooltip trigger="hover" placement="bottom">
<template #trigger> <template #trigger>
<n-button data-test-id="previous-otp" secondary @click.prevent="copyPrevious(tokens.previous)">{{ <c-button important:h-12 data-test-id="previous-otp" @click.prevent="copyPrevious(tokens.previous)">
tokens.previous {{ tokens.previous }}
}}</n-button> </c-button>
</template> </template>
<div>{{ previousCopied ? 'Copied !' : 'Copy previous OTP' }}</div> <div>{{ previousCopied ? 'Copied !' : 'Copy previous OTP' }}</div>
</n-tooltip> </n-tooltip>
<n-tooltip trigger="hover" placement="bottom"> <n-tooltip trigger="hover" placement="bottom">
<template #trigger> <template #trigger>
<n-button <c-button
tertiary
type="primary"
data-test-id="current-otp" data-test-id="current-otp"
class="current-otp" class="current-otp"
important:h-12
@click.prevent="copyCurrent(tokens.current)" @click.prevent="copyCurrent(tokens.current)"
> >
{{ tokens.current }} {{ tokens.current }}
</n-button> </c-button>
</template> </template>
<div>{{ currentCopied ? 'Copied !' : 'Copy current OTP' }}</div> <div>{{ currentCopied ? 'Copied !' : 'Copy current OTP' }}</div>
</n-tooltip> </n-tooltip>
<n-tooltip trigger="hover" placement="bottom"> <n-tooltip trigger="hover" placement="bottom">
<template #trigger> <template #trigger>
<n-button secondary data-test-id="next-otp" @click.prevent="copyNext(tokens.next)">{{ <c-button important:h-12 data-test-id="next-otp" @click.prevent="copyNext(tokens.next)">{{
tokens.next tokens.next
}}</n-button> }}</c-button>
</template> </template>
<div>{{ nextCopied ? 'Copied !' : 'Copy next OTP' }}</div> <div>{{ nextCopied ? 'Copied !' : 'Copy next OTP' }}</div>
</n-tooltip> </n-tooltip>

View file

@ -28,7 +28,7 @@
<n-gi> <n-gi>
<n-space justify="center" align="center" vertical> <n-space justify="center" align="center" vertical>
<n-image :src="qrcode" width="200" /> <n-image :src="qrcode" width="200" />
<n-button secondary @click="download"> Download qr-code </n-button> <c-button @click="download"> Download qr-code </c-button>
</n-space> </n-space>
</n-gi> </n-gi>
</n-grid> </n-grid>

View file

@ -4,8 +4,8 @@
{{ port }} {{ port }}
</div> </div>
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copy"> Copy </n-button> <c-button @click="copy"> Copy </c-button>
<n-button secondary @click="refreshPort"> Refresh </n-button> <c-button @click="refreshPort"> Refresh </c-button>
</n-space> </n-space>
</n-card> </n-card>
</template> </template>

View file

@ -8,9 +8,9 @@
<div class="result"> <div class="result">
{{ outputRoman }} {{ outputRoman }}
</div> </div>
<n-button secondary autofocus :disabled="validationNumeral.validationStatus === 'error'" @click="copyRoman"> <c-button autofocus :disabled="validationNumeral.validationStatus === 'error'" @click="copyRoman">
Copy Copy
</n-button> </c-button>
</n-space> </n-space>
</n-card> </n-card>
<br /> <br />
@ -22,9 +22,7 @@
<div class="result"> <div class="result">
{{ outputNumeral }} {{ outputNumeral }}
</div> </div>
<n-button secondary autofocus :disabled="validationRoman.validationStatus === 'error'" @click="copyArabic"> <c-button :disabled="validationRoman.validationStatus === 'error'" @click="copyArabic"> Copy </c-button>
Copy
</n-button>
</n-space> </n-space>
</n-card> </n-card>
</div> </div>

View file

@ -5,7 +5,7 @@
<n-input-number v-model:value="bits" min="256" max="16384" step="8" /> <n-input-number v-model:value="bits" min="256" max="16384" step="8" />
</n-form-item> </n-form-item>
<n-button tertiary @click="refreshCerts">Refresh key-pair</n-button> <c-button @click="refreshCerts">Refresh key-pair</c-button>
</n-space> </n-space>
</div> </div>

View file

@ -14,7 +14,7 @@
</n-form-item> </n-form-item>
<n-space justify="center"> <n-space justify="center">
<n-button secondary :disabled="slug.length === 0" @click="copy">Copy slug</n-button> <c-button :disabled="slug.length === 0" @click="copy">Copy slug</c-button>
</n-space> </n-space>
</div> </div>
</template> </template>

View file

@ -38,9 +38,9 @@
</n-form-item> </n-form-item>
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copySVG()">Copy svg</n-button> <c-button @click="copySVG()">Copy svg</c-button>
<n-button secondary @click="copyBase64()">Copy base64</n-button> <c-button @click="copyBase64()">Copy base64</c-button>
<n-button secondary @click="download()">Download svg</n-button> <c-button @click="download()">Download svg</c-button>
</n-space> </n-space>
</div> </div>

View file

@ -11,7 +11,7 @@
</n-card> </n-card>
<n-space justify="center"> <n-space justify="center">
<n-button secondary autofocus @click="copy"> Copy NATO string </n-button> <c-button autofocus @click="copy"> Copy NATO string </c-button>
</n-space> </n-space>
</n-space> </n-space>
</div> </div>

View file

@ -44,8 +44,8 @@
<br /> <br />
<br /> <br />
<n-space justify="center"> <n-space justify="center">
<n-button secondary autofocus @click="copy"> Copy </n-button> <c-button @click="copy"> Copy </c-button>
<n-button secondary @click="refreshToken"> Refresh </n-button> <c-button @click="refreshToken"> Refresh </c-button>
</n-space> </n-space>
</n-card> </n-card>
</div> </div>

View file

@ -24,7 +24,7 @@
</n-form-item> </n-form-item>
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copyEncoded"> Copy </n-button> <c-button @click="copyEncoded"> Copy </c-button>
</n-space> </n-space>
</n-card> </n-card>
<n-card title="Decode"> <n-card title="Decode">
@ -52,7 +52,7 @@
</n-form-item> </n-form-item>
<n-space justify="center"> <n-space justify="center">
<n-button secondary @click="copyDecoded"> Copy </n-button> <c-button @click="copyDecoded"> Copy </c-button>
</n-space> </n-space>
</n-card> </n-card>
</template> </template>

View file

@ -19,8 +19,8 @@
/> />
<n-space justify="center"> <n-space justify="center">
<n-button secondary autofocus @click="copy"> Copy </n-button> <c-button autofocus @click="copy"> Copy </c-button>
<n-button secondary @click="refreshUUIDs"> Refresh </n-button> <c-button @click="refreshUUIDs"> Refresh </c-button>
</n-space> </n-space>
</n-space> </n-space>
</template> </template>

View file

@ -0,0 +1,240 @@
import { defineThemes } from '../theme/theme.models';
import { appThemes } from '../theme/themes';
export const { useTheme } = defineThemes({
dark: {
basic: {
default: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: 'rgba(255, 255, 255, 0.08)',
hover: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: 'rgba(255, 255, 255, 0.12)',
},
pressed: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: 'rgba(255, 255, 255, 0.24)',
},
outline: {
color: appThemes.dark.primary.color,
},
},
primary: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.primary.color,
hover: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.primary.colorHover,
},
pressed: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.primary.colorPressed,
},
outline: {
color: appThemes.dark.primary.color,
},
},
warning: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.warning.color,
hover: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.warning.colorHover,
},
pressed: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.warning.colorPressed,
},
outline: {
color: appThemes.dark.warning.color,
},
},
},
text: {
default: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: 'transparent',
hover: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: 'rgba(255, 255, 255, 0.12)',
},
pressed: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: 'rgba(255, 255, 255, 0.82)',
},
outline: {
color: appThemes.dark.primary.color,
},
},
primary: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.primary.color,
hover: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.primary.colorHover,
},
pressed: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.primary.colorPressed,
},
outline: {
color: appThemes.dark.primary.color,
},
},
warning: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.warning.color,
hover: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.warning.colorHover,
},
pressed: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.dark.warning.colorPressed,
},
outline: {
color: appThemes.dark.warning.color,
},
},
},
},
light: {
basic: {
default: {
textColor: appThemes.light.text.baseColor,
backgroundColor: 'rgba(46, 51, 56, 0.05)',
hover: {
textColor: appThemes.light.text.baseColor,
backgroundColor: 'rgba(46, 51, 56, 0.09)',
},
pressed: {
textColor: appThemes.light.text.baseColor,
backgroundColor: 'rgba(46, 51, 56, 0.22)',
},
outline: {
color: appThemes.light.primary.color,
},
},
primary: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.light.primary.color,
hover: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.light.primary.colorHover,
},
pressed: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.light.primary.colorPressed,
},
outline: {
color: appThemes.light.primary.color,
},
},
warning: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.light.warning.color,
hover: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.light.warning.colorHover,
},
pressed: {
textColor: appThemes.dark.text.baseColor,
backgroundColor: appThemes.light.warning.colorPressed,
},
outline: {
color: appThemes.light.warning.color,
},
},
},
text: {
default: {
textColor: appThemes.light.text.baseColor,
backgroundColor: 'transparent',
hover: {
textColor: appThemes.light.text.baseColor,
backgroundColor: 'rgba(46, 51, 56, 0.09)',
},
pressed: {
textColor: appThemes.light.text.baseColor,
backgroundColor: 'rgba(46, 51, 56, 0.13)',
},
outline: {
color: appThemes.light.primary.color,
},
},
primary: {
textColor: appThemes.light.primary.color,
backgroundColor: 'transparent',
hover: {
textColor: appThemes.light.primary.colorHover,
backgroundColor: 'rgba(46, 51, 56, 0.09)',
},
pressed: {
textColor: appThemes.light.primary.colorPressed,
backgroundColor: 'rgba(46, 51, 56, 0.13)',
},
outline: {
color: appThemes.light.primary.color,
},
},
warning: {
textColor: appThemes.light.warning.color,
backgroundColor: 'transparent',
hover: {
textColor: appThemes.light.warning.colorHover,
backgroundColor: 'rgba(46, 51, 56, 0.09)',
},
pressed: {
textColor: appThemes.light.warning.colorPressed,
backgroundColor: 'rgba(46, 51, 56, 0.13)',
},
outline: {
color: appThemes.light.warning.color,
},
},
},
},
});

View file

@ -0,0 +1,113 @@
<template>
<component
:is="tag"
:href="href ?? to"
class="c-button"
:class="{ disabled, round, circle }"
:to="to"
@click="handleClick"
>
<slot />
</component>
</template>
<script lang="ts" setup>
import type { RouteLocationRaw } from 'vue-router';
import { useTheme } from './c-button.theme';
const props = withDefaults(
defineProps<{
type?: 'default' | 'primary';
variant?: 'basic' | 'text';
disabled?: boolean;
round?: boolean;
circle?: boolean;
href?: string;
to?: RouteLocationRaw;
}>(),
{
type: 'default',
variant: 'basic',
disabled: false,
round: false,
circle: false,
href: undefined,
to: undefined,
},
);
const { variant, disabled, round, circle, href, type, to } = toRefs(props);
const emits = defineEmits(['click']);
function handleClick(event: MouseEvent) {
if (!disabled.value) {
emits('click', event);
}
}
const theme = useTheme();
const variantTheme = computed(() => theme.value[variant.value][type.value]);
const tag = computed(() => {
if (href.value) {
return 'a';
}
if (to.value) {
return 'router-link';
}
return 'button';
});
</script>
<style lang="less" scoped>
.c-button {
margin: 0;
line-height: 1;
font-family: inherit;
font-size: inherit;
border: none;
text-align: center;
cursor: pointer;
text-decoration: none;
height: 34px;
font-weight: 400;
color: v-bind('variantTheme.textColor');
padding: 0 14px;
border-radius: 4px;
transition: background-color cubic-bezier(0.4, 0, 0.2, 1) 0.3s;
background-color: v-bind('variantTheme.backgroundColor');
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: center;
// outline-offset: 1px;
&.round {
border-radius: 100px;
}
&.circle {
border-radius: 40px;
width: 34px;
}
&:not(.disabled) {
&:hover {
background-color: v-bind('variantTheme.hover.backgroundColor');
}
&:active {
background-color: v-bind('variantTheme.pressed.backgroundColor');
}
}
&:focus {
outline: 2px solid v-bind('variantTheme.outline.color');
}
&.disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
</style>

View file

@ -0,0 +1,39 @@
import { defineThemes } from '../theme/theme.models';
import { appThemes } from '../theme/themes';
export const { useTheme } = defineThemes({
dark: {
default: {
textColor: appThemes.dark.primary.color,
hover: {
textColor: appThemes.dark.primary.colorHover,
},
pressed: {
textColor: appThemes.dark.primary.colorPressed,
},
outline: {
color: appThemes.dark.primary.color,
},
},
},
light: {
default: {
textColor: appThemes.light.primary.color,
hover: {
textColor: appThemes.light.primary.colorHover,
},
pressed: {
textColor: appThemes.light.primary.colorPressed,
},
outline: {
color: appThemes.light.primary.color,
},
},
},
});

49
src/ui/c-link/c-link.vue Normal file
View file

@ -0,0 +1,49 @@
<template>
<component :is="tag" :href="href ?? to" class="c-link" :to="to">
<slot />
</component>
</template>
<script lang="ts" setup>
import { RouterLink, type RouteLocationRaw } from 'vue-router';
import { useTheme } from './c-link.theme';
const props = defineProps<{
href?: string;
to?: RouteLocationRaw;
}>();
const { href, to } = toRefs(props);
const theme = useTheme();
const tag = computed(() => (href?.value ? 'a' : RouterLink));
</script>
<style lang="less" scoped>
.c-link {
line-height: inherit;
font-family: inherit;
font-size: inherit;
border: none;
cursor: pointer;
text-decoration: none;
font-weight: 400;
color: v-bind('theme.default.textColor');
border-radius: 4px;
transition: color cubic-bezier(0.4, 0, 0.2, 1) 0.3s;
outline-offset: 1px;
&:hover {
color: v-bind('theme.default.hover.textColor');
}
&:active {
color: v-bind('theme.default.textColor');
}
&:focus {
color: v-bind('theme.default.outline.color');
}
}
</style>

View file

@ -0,0 +1,13 @@
import { useThemeStore } from './theme.store';
export { defineThemes };
function defineThemes<Theme>(themes: { light: Theme; dark: Theme }) {
return {
themes,
useTheme() {
const themeStore = useThemeStore();
return computed(() => themes[themeStore.themeType]);
},
};
}

View file

@ -0,0 +1,20 @@
import { defineStore } from 'pinia';
export const useThemeStore = defineStore('ui-theme', {
state: () => ({
themeType: useStorage<'dark' | 'light'>('ui-store:theme-type', 'dark') as Ref<'dark' | 'light'>,
}),
getters: {
isDarkTheme(): boolean {
return this.themeType === 'dark';
},
isLightTheme(): boolean {
return this.themeType === 'light';
},
},
actions: {
toggleTheme() {
this.themeType = this.isDarkTheme ? 'light' : 'dark';
},
},
});

37
src/ui/theme/themes.ts Normal file
View file

@ -0,0 +1,37 @@
import { defineThemes } from './theme.models';
export const { themes: appThemes, useTheme: useAppTheme } = defineThemes({
light: {
text: {
baseColor: 'rgb(51, 54, 57)',
},
primary: {
color: '#18a058',
colorHover: '#1ea54c',
colorPressed: '#0C7A43',
},
warning: {
color: '#f59e0b',
colorHover: '#f59e0b',
colorPressed: '#f59e0b',
},
},
dark: {
text: {
baseColor: 'rgba(255, 255, 255, 0.82)',
},
primary: {
color: '#1ea54c',
colorHover: '#36AD6A',
colorPressed: '#0C7A43',
},
warning: {
color: '#f59e0b',
colorHover: '#f59e0b',
colorPressed: '#f59e0b',
},
},
});

View file

@ -73,6 +73,9 @@ export default defineConfig({
}, },
}), }),
Components({ Components({
dirs: ['src/'],
extensions: ['vue', 'md'],
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
resolvers: [NaiveUiResolver()], resolvers: [NaiveUiResolver()],
}), }),
Unocss(), Unocss(),