mirror of
https://github.com/Macrame-App/Macrame
synced 2025-12-29 07:19:26 +00:00
Base components added
This commit is contained in:
parent
a6024f22e7
commit
9239c77e12
6 changed files with 215 additions and 58 deletions
54
fe/src/components/base/AlertComp.vue
Normal file
54
fe/src/components/base/AlertComp.vue
Normal 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>
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
15
fe/src/components/base/ButtonGroup.vue
Normal file
15
fe/src/components/base/ButtonGroup.vue
Normal 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>
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue