refactor to UI components

This commit is contained in:
2023-05-26 00:50:19 +02:00
parent 0f48494469
commit c76bf3f6d8
20 changed files with 160 additions and 78 deletions

4
components.d.ts vendored
View File

@@ -31,10 +31,14 @@ declare module '@vue/runtime-core' {
Spinner: typeof import('./src/components/Spinner.vue')['default'] Spinner: typeof import('./src/components/Spinner.vue')['default']
TopBar: typeof import('./src/components/TopBar.vue')['default'] TopBar: typeof import('./src/components/TopBar.vue')['default']
UIAlert: typeof import('./src/components/ui/UIAlert.vue')['default'] UIAlert: typeof import('./src/components/ui/UIAlert.vue')['default']
UIBadge: typeof import('./src/components/ui/UIBadge.vue')['default']
UIButton: typeof import('./src/components/ui/UIButton.vue')['default'] UIButton: typeof import('./src/components/ui/UIButton.vue')['default']
UIButtonGroup: typeof import('./src/components/ui/UIButtonGroup.vue')['default'] UIButtonGroup: typeof import('./src/components/ui/UIButtonGroup.vue')['default']
UICard: typeof import('./src/components/ui/UICard.vue')['default']
UIDropdown: typeof import('./src/components/ui/UIDropdown.vue')['default'] UIDropdown: typeof import('./src/components/ui/UIDropdown.vue')['default']
UIDropdownItem: typeof import('./src/components/ui/UIDropdownItem.vue')['default'] UIDropdownItem: typeof import('./src/components/ui/UIDropdownItem.vue')['default']
UIMenu: typeof import('./src/components/ui/UIMenu.vue')['default']
UIMenuItem: typeof import('./src/components/ui/UIMenuItem.vue')['default']
UIModal: typeof import('./src/components/ui/UIModal.vue')['default'] UIModal: typeof import('./src/components/ui/UIModal.vue')['default']
UITable: typeof import('./src/components/ui/UITable.vue')['default'] UITable: typeof import('./src/components/ui/UITable.vue')['default']
UITextInput: typeof import('./src/components/ui/UITextInput.vue')['default'] UITextInput: typeof import('./src/components/ui/UITextInput.vue')['default']

View File

@@ -45,16 +45,11 @@ const handleKeypress = (event: { [key: string]: number }) => {
defineExpose({ handleKeypress }) defineExpose({ handleKeypress })
</script> </script>
<template> <template>
<ul <UIMenu class="border-[1px] p-2 text-[0.875rem] text-black shadow-md" :compact="true">
tabindex="0" <UIMenuItem :active="!activeResult" @click="emit('createLink', props.autocompleteText)">
class="menu rounded-md border-[1px] bg-base-100 p-2 text-[0.875rem] text-black shadow-md" <span class="flex-grow">{{ props.autocompleteText }}</span>
> <i class="fas fa-plus-circle ml-auto text-white" />
<li class="flex flex-row" @click="emit('createLink', props.autocompleteText)"> </UIMenuItem>
<a class="flex-grow px-2 py-1" :class="!activeResult && 'active'">
<span class="flex-grow">{{ props.autocompleteText }}</span>
<i class="fas fa-plus-circle ml-auto text-white" />
</a>
</li>
<SearchResult <SearchResult
v-for="result in results" v-for="result in results"
:key="result.id" :key="result.id"
@@ -62,5 +57,5 @@ defineExpose({ handleKeypress })
:active-result="activeResult" :active-result="activeResult"
@go-to-note="emit('createLink', result.title)" @go-to-note="emit('createLink', result.title)"
/> />
</ul> </UIMenu>
</template> </template>

View File

@@ -5,22 +5,18 @@ const props = defineProps<{
}>() }>()
</script> </script>
<template> <template>
<div class="card mt-3 border-[1px]" v-if="props.references.length > 0"> <UIMenu class="mt-3 rounded-xl border-[1px] px-3 py-3" v-if="props.references.length > 0">
<div class="card-body px-3 py-3"> <UIMenuItem :title="true">
<ul class="menu rounded-md"> <span>References</span>
<li class="menu-title !opacity-100"> <UIBadge variant="outline">{{ props.references.length }}</UIBadge>
<span class="card-title text-secondary"> </UIMenuItem>
References <UIMenuItem
<div class="badge-outline badge">{{ props.references.length }}</div> v-for="reference in props.references"
</span> :key="reference.id"
</li> @click="setActiveNote(reference.id)"
<li v-for="reference in props.references" :key="reference.id"> >
<a class="rounded-md" @click="setActiveNote(reference.id)"> <i class="far fa-file-alt fa-fw" />
<i class="far fa-file-alt fa-fw" /> {{ reference.title }}
{{ reference.title }} </UIMenuItem>
</a> </UIMenu>
</li>
</ul>
</div>
</div>
</template> </template>

View File

@@ -66,7 +66,7 @@ const resultsRefs = ref<InstanceType<typeof SearchResult>[]>([])
@keydown="handleKeydown" @keydown="handleKeydown"
/> />
<div class="z-1000 absolute left-0 right-0 top-[100%]" v-if="active"> <div class="z-1000 absolute left-0 right-0 top-[100%]" v-if="active">
<ul tabindex="0" class="menu mt-1 w-full rounded-md bg-base-100 p-2 text-black shadow"> <UIMenu :compact="true" class="mt-1 w-full rounded-md bg-base-100 p-2 text-black shadow">
<div class="max-h-[320px] w-full overflow-y-auto"> <div class="max-h-[320px] w-full overflow-y-auto">
<template v-if="results.length > 0"> <template v-if="results.length > 0">
<SearchResult <SearchResult
@@ -78,9 +78,9 @@ const resultsRefs = ref<InstanceType<typeof SearchResult>[]>([])
ref="resultsRefs" ref="resultsRefs"
/> />
</template> </template>
<li v-else><a>No notes found</a></li> <UIMenuItem :compact="true" v-else>No notes found</UIMenuItem>
</div> </div>
</ul> </UIMenu>
</div> </div>
</div> </div>
</template> </template>

View File

@@ -14,21 +14,15 @@ const emit = defineEmits<{
const element = ref<HTMLElement | null>(null) const element = ref<HTMLElement | null>(null)
</script> </script>
<template> <template>
<li class="flex w-full flex-row" ref="element"> <UIMenuItem
<a class="flex w-full items-center"
class="items-center px-2 py-1 w-full" @click.stop.prevent="() => emit('goToNote', element)"
@click.stop.prevent="() => emit('goToNote', element)" @mousedown.prevent
@mousedown.prevent :disabled="activeNote?.id === result.id"
:class="{ :active="props.activeResult?.id === result.id"
disabled: activeNote?.id === result.id, >
active: props.activeResult?.id === result.id <UIBadge size="sm" variant="ghost" class="mr-0.5" v-if="activeNote?.id === result.id">current</UIBadge>
}" <span class="flex-grow overflow-hidden whitespace-nowrap">{{ result.title }}</span>
> <span class="whitespace-nowrap">{{ formatDate(result.modified) }}</span>
<span class="badge-ghost badge badge-sm mr-0.5" v-if="activeNote?.id === result.id"> </UIMenuItem>
current
</span>
<span class="flex-grow overflow-hidden whitespace-nowrap">{{ result.title }}</span>
<span class="whitespace-nowrap">{{ formatDate(result.modified) }}</span>
</a>
</li>
</template> </template>

View File

@@ -41,7 +41,7 @@ const authModalInitialStateOpen = ref<boolean>(authUI.isPendingRedirect())
<UIButton <UIButton
size="sm" size="sm"
variant="outline" variant="outline"
class="search-active-hide py-1 text-white" class="search-active-hide py-1 text-white topbar-button"
@click="addNote('Untitled new note', '', true)" @click="addNote('Untitled new note', '', true)"
> >
<i class="fas fa-plus-circle text-[1.1rem]" /> <i class="fas fa-plus-circle text-[1.1rem]" />
@@ -51,7 +51,7 @@ const authModalInitialStateOpen = ref<boolean>(authUI.isPendingRedirect())
<UIButton <UIButton
size="sm" size="sm"
variant="outline" variant="outline"
class="search-active-hide py-1 text-white" class="search-active-hide py-1 text-white topbar-button"
@click="open" @click="open"
> >
Sign in Sign in
@@ -79,7 +79,7 @@ const authModalInitialStateOpen = ref<boolean>(authUI.isPendingRedirect())
#logo:hover { #logo:hover {
text-shadow: 0 0 5px white, 0 0 10px white, 0 0 15px white; text-shadow: 0 0 5px white, 0 0 10px white, 0 0 15px white;
} }
.btn-outline { .topbar-button {
@apply hover:border-white hover:bg-white hover:text-primary focus-visible:outline-white; @apply hover:border-white hover:bg-white hover:text-primary focus-visible:outline-white;
} }

View File

@@ -26,7 +26,7 @@ const handleClick = (fn: (...args: any[]) => any) => {
<OnClickOutside> <OnClickOutside>
<UIDropdown class="search-active-hide"> <UIDropdown class="search-active-hide">
<template #activator> <template #activator>
<UIButton :dropdown="true" size="sm" variant="outline" class="py-1 text-white"> <UIButton :dropdown="true" size="sm" variant="outline" class="py-1 text-white topbar-button">
<i class="fa-fw fa-solid fa-user-gear" /> <i class="fa-fw fa-solid fa-user-gear" />
</UIButton> </UIButton>
</template> </template>

View File

@@ -93,10 +93,10 @@ const deleteSelectedNotes = (closeModal: () => void) => {
</td> </td>
<td>{{ note.wordCount }}</td> <td>{{ note.wordCount }}</td>
<td> <td>
<div class="badge" v-if="note.references.length > 0"> <UIBadge v-if="note.references.length > 0">
<i data-v-41bbc26f="" class="fas fa-fw fa-sign-out-alt mr-1"></i> <i data-v-41bbc26f="" class="fas fa-fw fa-sign-out-alt mr-1"></i>
{{ note.references.length }} {{ note.references.length }}
</div> </UIBadge>
</td> </td>
<td>{{ formatDate(note.modified) }}</td> <td>{{ formatDate(note.modified) }}</td>
</tr> </tr>

View File

@@ -7,12 +7,12 @@ const props = withDefaults(defineProps<Props>(), {
}) })
const styleClass = computed(() => { const styleClass = computed(() => {
const colorClass = `alert-${props.color}` const colorClass = `dui-alert-${props.color}`
return [colorClass] return [colorClass]
}) })
</script> </script>
<template> <template>
<div class="alert shadow-lg" :class="styleClass"> <div class="dui-alert shadow-lg" :class="styleClass">
<div><slot></slot></div> <div><slot></slot></div>
</div> </div>
</template> </template>

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
interface Props {
size: 'xs' | 'sm' | 'md' | 'lg'
variant?: 'regular' | 'outline' | 'ghost'
}
const props = withDefaults(defineProps<Props>(), {
size: 'md',
variant: 'regular'
})
const styleClass = computed(() => {
const sizeVariants = {
xs: 'dui-badge-xs',
sm: 'dui-badge-sm',
md: 'dui-badge-md',
lg: 'dui-badge-lg'
}
const variantVariants = {
regular: '',
outline: 'dui-badge-outline',
ghost: 'dui-badge-ghost'
}
const sizeClass = sizeVariants[props.size]
const variantClass = variantVariants[props.variant]
return [sizeClass, variantClass]
})
</script>
<template>
<div class="dui-badge" :class="styleClass"><slot></slot></div>
</template>

View File

@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
interface Props { interface Props {
size?: string size?: 'xs' | 'sm' | 'md' | 'lg'
variant?: 'regular' | 'outline' variant?: 'regular' | 'outline'
color?: string color?: 'regular' | 'primary'
dropdown?: boolean dropdown?: boolean
} }
@@ -14,18 +14,29 @@ const props = withDefaults(defineProps<Props>(), {
}) })
const styleClass = computed(() => { const styleClass = computed(() => {
const colorVariants: { [key: string]: string } = { const sizeVariants = {
primary: 'btn-primary' xs: 'dui-btn-xs',
sm: 'dui-btn-sm',
md: 'dui-btn-md',
lg: 'dui-btn-lg'
} }
const sizeClass = `btn-${props.size}` const colorVariants = {
const variantClass = props.variant !== 'regular' ? `btn-${props.variant}` : '' regular: '',
const colorClass = props.color !== 'regular' ? colorVariants[props.color] : '' primary: 'dui-btn-primary'
}
const variantVariants = {
regular: '',
outline: 'dui-btn-outline'
}
const sizeClass = sizeVariants[props.size]
const variantClass = variantVariants[props.variant]
const colorClass = colorVariants[props.color]
return [sizeClass, variantClass, colorClass] return [sizeClass, variantClass, colorClass]
}) })
</script> </script>
<template> <template>
<label class="btn duration-0" :class="styleClass" v-if="props.dropdown" tabindex="0"> <label class="dui-btn duration-0" :class="styleClass" v-if="props.dropdown" tabindex="0">
<slot></slot> <slot></slot>
</label> </label>
<button class="btn duration-0" :class="styleClass" v-else><slot></slot></button> <button class="dui-btn duration-0" :class="styleClass" v-else><slot></slot></button>
</template> </template>

View File

@@ -1,3 +1,3 @@
<template> <template>
<div class="btn-group"><slot></slot></div> <div class="dui-btn-group"><slot></slot></div>
</template> </template>

View File

@@ -1,9 +1,9 @@
<template> <template>
<div class="dropdown-end dropdown"> <div class="dui-dropdown-end dui-dropdown">
<slot name="activator" tabindex="0"></slot> <slot name="activator" tabindex="0"></slot>
<ul <ul
tabindex="0" tabindex="0"
class="dropdown-content menu rounded-box menu-compact mt-1 w-52 bg-base-100 p-2 text-base-content shadow" class="dui-dropdown-content dui-menu dui-menu-compact rounded-box mt-1 w-52 bg-base-100 p-2 text-base-content shadow"
> >
<slot name="items"></slot> <slot name="items"></slot>
</ul> </ul>

View File

@@ -1,5 +1,5 @@
<template> <template>
<li class="text-base"> <li class="text-base">
<a><slot></slot></a> <a class="rounded-lg"><slot></slot></a>
</li> </li>
</template> </template>

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
interface Props {
compact?: boolean
}
const props = defineProps<Props>()
const styleClass = computed(() => {
const compactClass = props.compact && 'dui-menu-compact'
return [compactClass]
})
</script>
<template>
<ul tabindex="0" class="dui-menu rounded-md bg-base-100" :class="styleClass">
<slot></slot>
</ul>
</template>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
interface Props {
title?: boolean
disabled?: boolean
active?: boolean
}
const props = defineProps<Props>()
const styleClass = computed(() => {
const titleClass = props.title
? 'dui-menu-item dui-menu-title !opacity-100 space-x-2 !text-xl font-bold text-secondary'
: ''
return [titleClass]
})
</script>
<template>
<li :class="styleClass">
<span class="flex items-center" v-if="props.title"><slot></slot></span>
<a
class="w-full rounded-md"
:class="{ 'dui-disabled': props.disabled, 'dui-active': props.active }"
v-else
>
<slot></slot>
</a>
</li>
</template>

View File

@@ -52,13 +52,13 @@ defineExpose({ open, close })
<slot name="activator" v-bind="slotProps"></slot> <slot name="activator" v-bind="slotProps"></slot>
<Teleport to="body"> <Teleport to="body">
<Transition @enter="onEnter" @leave="onLeave" appear> <Transition @enter="onEnter" @leave="onLeave" appear>
<div class="modal bg-neutral-800 bg-opacity-60" v-if="show" ref="modal"> <div class="dui-modal bg-neutral-800 bg-opacity-60" v-if="show" ref="modal">
<div class="modal-box" ref="modalBox"> <div class="dui-modal-box" ref="modalBox">
<h3 class="text-lg font-bold" v-if="$slots.title"><slot name="title" /></h3> <h3 class="text-lg font-bold" v-if="$slots.title"><slot name="title" /></h3>
<p class="py-4"> <p class="py-4">
<slot v-bind="slotProps" /> <slot v-bind="slotProps" />
</p> </p>
<div class="modal-action"> <div class="dui-modal-action">
<slot name="actions" v-bind="slotProps"> <slot name="actions" v-bind="slotProps">
<UIButton size="sm" @click="close">Close</UIButton> <UIButton size="sm" @click="close">Close</UIButton>
</slot> </slot>

View File

@@ -5,11 +5,11 @@ interface Props {
const props = defineProps<Props>() const props = defineProps<Props>()
const styleClass = computed(() => { const styleClass = computed(() => {
const densityClass = props.density ? 'table-compact' : '' const densityClass = props.density ? 'dui-table-compact' : ''
return [densityClass] return [densityClass]
}) })
</script> </script>
<template> <template>
<table class="table" :class="styleClass"><slot></slot></table> <table class="dui-table" :class="styleClass"><slot></slot></table>
</template> </template>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
interface Props { interface Props {
modelValue: any modelValue: any
size?: 'sm' | 'md' | 'lg' | 'xl' size?: 'xs' | 'sm' | 'md' | 'lg'
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@@ -13,7 +13,13 @@ const emit = defineEmits<{
}>() }>()
const styleClass = computed(() => { const styleClass = computed(() => {
const sizeClass = `input-${props.size}` const sizeVariants = {
'xs': 'dui-input-xs',
'sm': 'dui-input-sm',
'md': 'dui-input-md',
'lg': 'dui-input-lg'
}
const sizeClass = sizeVariants[props.size]
return [sizeClass] return [sizeClass]
}) })
</script> </script>
@@ -22,7 +28,7 @@ const styleClass = computed(() => {
type="text" type="text"
:value="props.modelValue" :value="props.modelValue"
@input="emit('update:modelValue', ($event.target as HTMLInputElement)?.value)" @input="emit('update:modelValue', ($event.target as HTMLInputElement)?.value)"
class="input-bordered input input-sm my-1 ml-auto mr-1 max-w-xs flex-grow" class="dui-input-bordered dui-input my-1 ml-auto mr-1 max-w-xs flex-grow"
:class="styleClass" :class="styleClass"
/> />
</template> </template>

View File

@@ -31,6 +31,7 @@ export default {
}, },
plugins: [require('daisyui')], plugins: [require('daisyui')],
daisyui: { daisyui: {
prefix: 'dui-',
themes: [ themes: [
{ {
contexted: { contexted: {