Rework to use marbas and authentik instead of poketbase (#1)
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
59
src/components/Dropdown.vue
Normal file
59
src/components/Dropdown.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/outline';
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const isOpen = ref(false);
|
||||
|
||||
useEventListener('keyup', e => {
|
||||
if (e.key === 'Escape') {
|
||||
isOpen.value = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dropdown" :class="{'dropdown-open': isOpen, 'dropdown-close': !isOpen}" v-on-click-outside="() => isOpen = false">
|
||||
<button @click="isOpen = !isOpen">
|
||||
<slot name="button" />
|
||||
<Transition name="flip">
|
||||
<ChevronDownIcon v-if="!isOpen" class="chevron" />
|
||||
<ChevronUpIcon v-else class="chevron" />
|
||||
</Transition>
|
||||
</button>
|
||||
|
||||
<Transition name="fade">
|
||||
<div v-if="isOpen" class="relative">
|
||||
<div class="z-10 divide-y rounded-b-md absolute">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.chevron {
|
||||
@apply w-4 h-4 ms-1;
|
||||
}
|
||||
|
||||
.fade-enter-from, .fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
@apply transition-opacity;
|
||||
}
|
||||
|
||||
.flip-enter-from, .flip-leave-to {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.flip-enter-active {
|
||||
@apply transition-transform;
|
||||
}
|
||||
.flip-leave-active {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -54,7 +54,7 @@ useEventListener('keyup', e => {
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity 100ms ease-out;
|
||||
@apply transition-opacity;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export { default as ClipboardButton } from './ClipboardButton.vue';
|
||||
export { default as Dropdown } from './Dropdown.vue';
|
||||
export { default as LoadingSpinner } from './LoadingSpinner.vue';
|
||||
export { default as Modal } from './Modal.vue';
|
||||
export { default as SliderCheckbox } from './SliderCheckbox.vue';
|
||||
|
||||
@@ -35,7 +35,7 @@ th {
|
||||
@apply relative h-8 pe-3;
|
||||
}
|
||||
span.asc, span.desc {
|
||||
@apply absolute end-1 cursor-pointer text-xs;
|
||||
@apply absolute end-1 cursor-pointer text-xs transition-opacity;
|
||||
}
|
||||
span.asc {
|
||||
@apply top-0.5;
|
||||
|
||||
44
src/components/table/sort.spec.ts
Normal file
44
src/components/table/sort.spec.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { describe, expect, test } from 'vitest'
|
||||
import { ref } from 'vue'
|
||||
import { useSort } from './sort'
|
||||
|
||||
describe('useSort', () => {
|
||||
const array = ref([{ key1: 'b', key2: 'a' }, { key1: 'a', key2: 'b' }])
|
||||
|
||||
test('Returns expected properties with default options', () => {
|
||||
const { sortedArray, headerProps, showColumn } = useSort(array)
|
||||
|
||||
expect(sortedArray).toBeDefined()
|
||||
expect(headerProps).toBeDefined()
|
||||
expect(showColumn).toBeDefined()
|
||||
})
|
||||
|
||||
test('Returns expected properties with custom options', () => {
|
||||
const { sortedArray, headerProps, showColumn } = useSort(array, { defaultSortKey: 'key1', defaultSortDirection: 'asc' })
|
||||
|
||||
expect(sortedArray.value[0].key1).toBe('a')
|
||||
expect(headerProps.value.currentSortKey).toBe('key1')
|
||||
expect(headerProps.value.sortDirection).toBe('asc')
|
||||
expect(showColumn('key1')).toBe(true)
|
||||
})
|
||||
|
||||
test('Sorts array in ascending order', () => {
|
||||
const { sortedArray, headerProps } = useSort(array, { defaultSortKey: 'key1', defaultSortDirection: 'asc' })
|
||||
|
||||
headerProps.value.onSort('key1', 'asc')
|
||||
expect(sortedArray.value[0].key1).toBe('a')
|
||||
})
|
||||
|
||||
test('Sorts array in descending order', () => {
|
||||
const { sortedArray, headerProps } = useSort(array, { defaultSortKey: 'key1', defaultSortDirection: 'desc' })
|
||||
|
||||
headerProps.value.onSort('key1', 'desc')
|
||||
expect(sortedArray.value[0].key1).toBe('b')
|
||||
})
|
||||
|
||||
test('Hides ignored columns', () => {
|
||||
const { showColumn } = useSort(array, { ignoredColums: ['key1'] })
|
||||
|
||||
expect(showColumn('key1')).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -16,7 +16,8 @@ export const useSort = <T>(array: MaybeRefOrGetter<T[]>, options?: UseSortOption
|
||||
};
|
||||
const showColumn = (sortKey: string) => !toValue(options?.ignoredColums)?.includes(sortKey);
|
||||
const headerProps = computed(() => ({
|
||||
onSort: sortBy, showColumn,
|
||||
onSort: sortBy,
|
||||
showColumn,
|
||||
currentSortKey: sortKey.value,
|
||||
sortDirection: sortDirection.value
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user