Base components added

This commit is contained in:
Jesse Malotaux 2025-03-23 14:47:50 +01:00
parent a6024f22e7
commit 9239c77e12
6 changed files with 215 additions and 58 deletions

View file

@ -0,0 +1,54 @@
<template>
<div :class="`alert alert__${type}`">
<IconInfoCircle v-if="type === 'info'" />
<IconCheck v-if="type === 'success'" />
<IconExclamationCircle v-if="type === 'warning'" />
<IconAlertTriangle v-if="type === 'error'" />
<slot />
</div>
</template>
<script setup>
import {
IconAlertTriangle,
IconCheck,
IconExclamationCircle,
IconInfoCircle,
} from '@tabler/icons-vue'
defineProps({
type: String, // info, success, warning, error
})
</script>
<style scoped>
@reference "@/assets/main.css";
.alert {
@apply flex
items-center
gap-4
p-4
border
border-white/10
bg-white/10
rounded-md
backdrop-blur-md;
&.alert__info {
@apply text-sky-400 bg-sky-400/10;
}
&.alert__success {
@apply text-lime-400 bg-lime-400/10;
}
&.alert__warning {
@apply text-amber-400 bg-amber-400/10;
}
&.alert__error {
@apply text-rose-400 bg-rose-400/10;
}
}
</style>

View file

@ -1,11 +1,11 @@
<template> <template>
<template v-if="href"> <template v-if="href">
<a :href="href" :class="`button ${classString}`"> <a :href="href" :class="classString">
<slot /> <slot />
</a> </a>
</template> </template>
<template v-else> <template v-else>
<button :class="`button ${classString}`"> <button :class="classString">
<slot /> <slot />
</button> </button>
</template> </template>
@ -21,23 +21,11 @@ const props = defineProps({
}) })
const classString = computed(() => { const classString = computed(() => {
const classes = { let classes = 'btn'
'bg-sky-500/80 hover:bg-sky-400 text-white border-sky-400': props.variant === 'primary', if (props.variant) classes += ` btn__${props.variant}`
'bg-white/80 hover:bg-white text-slate-900 border-white': props.variant === 'secondary', if (props.size) classes += ` btn__${props.size}`
'bg-red-700/80 hover:bg-red-700 text-white border-red-800': props.variant === 'danger',
'bg-slate-700/80 hover:bg-slate-700 text-white border-slate-600': props.variant === 'dark', return classes
'bg-lime-500/80 hover:bg-lime-500 text-white border-lime-600': props.variant === 'success',
'button__subtle bg-transparent hover:bg-white/10 text-white border-transparent':
props.variant === 'subtle',
'button__ghost bg-transparent text-white/80 border-transparent hover:text-white':
props.variant === 'ghost',
'button__lg px-5 py-3 text-lg gap-4': props.size === 'lg',
'button__sm px-3 py-1.5 text-sm gap-2': props.size === 'sm',
'px-4 py-2 gap-3': props.size !== 'sm' && props.size !== 'lg',
}
return Object.keys(classes)
.filter((key) => classes[key])
.join(' ')
}) })
</script> </script>
@ -45,32 +33,125 @@ const classString = computed(() => {
@reference "@/assets/main.css"; @reference "@/assets/main.css";
button, button,
.button { .btn {
@apply flex @apply flex
items-center items-center
gap-3
h-fit h-fit
px-4 py-2
border border
border-solid
rounded-lg rounded-lg
font-semibold tracking-wide
shadow-md font-normal
shadow-transparent transition-all
transition
cursor-pointer; cursor-pointer;
transition:
border-color 0.1s ease-in-out,
background-color 0.2s ease;
&:not(.button__subtle, .button__ghost):hover { &:not(.button__subtle, .button__ghost):hover {
@apply shadow-black; @apply shadow-black;
} }
svg { &[disabled],
@apply size-5 stroke-1; &.disabled {
@apply opacity-50 pointer-events-none cursor-not-allowed;
} }
&.button__sm svg { svg {
@apply size-5 transition-[stroke] duration-400 ease-in-out;
}
&.btn__sm svg {
@apply size-4; @apply size-4;
} }
&.button__lg svg { &.btn__lg svg {
@apply size-6; @apply size-6;
} }
&:hover {
@apply !text-white;
svg {
@apply !stroke-white;
}
}
&.btn__primary {
@apply bg-sky-100/10 border-sky-100 text-sky-100;
svg {
@apply stroke-sky-200;
}
&:hover {
@apply bg-sky-400/40 border-sky-300;
}
}
&.btn__secondary {
@apply bg-amber-100/10 border-amber-100 text-amber-100;
svg {
@apply stroke-amber-300;
}
&:hover {
@apply bg-amber-400/40 border-amber-400;
}
}
&.btn__danger {
@apply bg-rose-200/20 border-rose-100 text-rose-200;
svg {
@apply stroke-rose-400;
}
&:hover {
@apply bg-rose-400/40 border-rose-500 text-white;
}
}
&.btn__dark {
/* @apply bg-slate-700/80 hover:bg-slate-700 text-white border-slate-600; */
@apply bg-slate-200/10 border-slate-400 text-slate-100;
svg {
@apply stroke-slate-300;
}
&:hover {
@apply bg-slate-400/40 border-slate-200 text-white;
}
}
&.btn__success {
/* @apply bg-lime-500/80 hover:bg-lime-500 text-white border-lime-600; */
@apply bg-lime-200/10 border-lime-100 text-lime-100;
svg {
@apply stroke-lime-400;
}
&:hover {
@apply bg-lime-400/40 border-lime-500 text-white;
}
}
&.btn__subtle {
@apply bg-transparent hover:bg-white/10 text-white border-transparent;
&:hover {
@apply bg-white/20 to-white/30 border-white/40;
}
}
&.btn__ghost {
@apply bg-transparent text-white/80 border-transparent hover:text-white;
}
} }
</style> </style>

View file

@ -0,0 +1,15 @@
<template>
<div class="button-group">
<slot />
</div>
</template>
<script setup>
defineProps({
variant: String,
})
</script>
<style scoped>
@reference "@/assets/main.css";
</style>

View file

@ -1,6 +1,6 @@
<template> <template>
<div class="context-menu"> <div class="context-menu">
<div class="context-menu__trigger" @click="menuOpen = !menuOpen"> <div class="context-menu__trigger" @click="toggle">
<slot name="trigger" /> <slot name="trigger" />
</div> </div>
<div :class="`context-menu__content ${menuOpen ? 'open' : ''}`"> <div :class="`context-menu__content ${menuOpen ? 'open' : ''}`">
@ -10,7 +10,9 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted, onUpdated } from 'vue'
defineExpose({ toggle })
const props = defineProps({ const props = defineProps({
open: Boolean, open: Boolean,
@ -21,6 +23,12 @@ const menuOpen = ref(false)
onMounted(() => { onMounted(() => {
menuOpen.value = props.open menuOpen.value = props.open
}) })
function toggle() {
console.log('toggle')
menuOpen.value = !menuOpen.value
}
</script> </script>
<style> <style>
@ -55,7 +63,7 @@ onMounted(() => {
} }
} }
ul { .context-menu ul {
@apply text-slate-800 @apply text-slate-800
divide-y divide-y
divide-slate-300; divide-slate-300;

View file

@ -3,7 +3,7 @@
<div class="trigger" @click="toggleDialog(true)"> <div class="trigger" @click="toggleDialog(true)">
<slot name="trigger" /> <slot name="trigger" />
</div> </div>
<dialog ref="dialog"> <dialog ref="dialog" class="mcrm-block block__dark">
<ButtonComp <ButtonComp
class="dialog__close p-0" class="dialog__close p-0"
variant="ghost" variant="ghost"
@ -28,6 +28,8 @@ const openDialog = ref()
const emit = defineEmits(['onOpen', 'onClose', 'onToggle']) const emit = defineEmits(['onOpen', 'onClose', 'onToggle'])
defineExpose({ toggleDialog })
const props = defineProps({ const props = defineProps({
open: Boolean, open: Boolean,
}) })
@ -40,7 +42,7 @@ onUpdated(() => {
if (props.open === true) toggleDialog(props.open) if (props.open === true) toggleDialog(props.open)
}) })
const toggleDialog = (openToggle) => { function toggleDialog(openToggle) {
if (openToggle) { if (openToggle) {
dialog.value.showModal() dialog.value.showModal()
emit('onOpen') emit('onOpen')
@ -64,7 +66,7 @@ onMounted(() => {
}) })
</script> </script>
<style scoped> <style>
@reference "@/assets/main.css"; @reference "@/assets/main.css";
.dialog-container { .dialog-container {
@ -74,13 +76,10 @@ onMounted(() => {
@apply fixed @apply fixed
top-1/2 left-1/2 top-1/2 left-1/2
-translate-x-1/2 -translate-y-1/2 -translate-x-1/2 -translate-y-1/2
p-4 max-w-[calc(100vw-2rem)]
bg-slate-800 text-slate-200
border /* shadow-md */
border-slate-600 /* shadow-black */
rounded-lg
shadow-md
shadow-black
z-50 z-50
pointer-events-none; pointer-events-none;
@ -94,7 +93,7 @@ onMounted(() => {
.dialog__close { .dialog__close {
@apply absolute @apply absolute
top-2 right-2 top-4 right-4
p-0 p-0
text-white; text-white;
@ -104,4 +103,9 @@ onMounted(() => {
} }
} }
} }
.dialog__content {
> *:first-child {
@apply pr-8;
}
}
</style> </style>

View file

@ -1,8 +1,8 @@
<template> <template>
<nav id="main-menu"> <nav id="main-menu">
<button id="menu-toggle" :class="menuOpen ? 'open' : ''" @click="menuOpen = !menuOpen"> <button id="menu-toggle" :class="menuOpen ? 'open' : ''" @click="menuOpen = !menuOpen">
<IconMenu2 /> <img class="logo" src="@/assets/Macrame-Logo-gradient.svg" aria-hidden="true" />
<IconX /> <IconX :class="{ 'opacity-0': !menuOpen }" />
</button> </button>
<ul :class="menuOpen ? 'open' : ''"> <ul :class="menuOpen ? 'open' : ''">
<li> <li>
@ -14,6 +14,9 @@
<li> <li>
<RouterLink @click="menuOpen = false" to="/macros"> <IconKeyboard />Macros </RouterLink> <RouterLink @click="menuOpen = false" to="/macros"> <IconKeyboard />Macros </RouterLink>
</li> </li>
<li>
<RouterLink @click="menuOpen = false" to="/devices"> <IconDevices />Devices </RouterLink>
</li>
<li> <li>
<RouterLink @click="menuOpen = false" to="/settings"> <IconSettings />Settings </RouterLink> <RouterLink @click="menuOpen = false" to="/settings"> <IconSettings />Settings </RouterLink>
</li> </li>
@ -24,10 +27,10 @@
<script setup> <script setup>
import { RouterLink } from 'vue-router' import { RouterLink } from 'vue-router'
import { import {
IconDevices,
IconHome, IconHome,
IconKeyboard, IconKeyboard,
IconLayoutGrid, IconLayoutGrid,
IconMenu2,
IconSettings, IconSettings,
IconX, IconX,
} from '@tabler/icons-vue' } from '@tabler/icons-vue'
@ -36,7 +39,7 @@ import { ref } from 'vue'
const menuOpen = ref(false) const menuOpen = ref(false)
</script> </script>
<style scoped> <style>
@reference "@/assets/main.css"; @reference "@/assets/main.css";
nav { nav {
@apply relative flex z-50; @apply relative flex z-50;
@ -48,10 +51,12 @@ nav {
rounded-full rounded-full
aspect-square aspect-square
bg-white/20 hover:bg-white/40 bg-white/20 hover:bg-white/40
border-0
cursor-pointer cursor-pointer
transition-colors transition-colors
backdrop-blur-md; backdrop-blur-md;
.logo,
svg { svg {
@apply absolute @apply absolute
inset-1/2 inset-1/2
@ -61,18 +66,8 @@ nav {
ease-in-out; ease-in-out;
} }
svg:last-of-type { .logo {
@apply opacity-0; @apply w-full;
}
&.open {
svg:first-of-type {
@apply opacity-0;
}
svg:last-of-type {
@apply opacity-100;
}
} }
} }