feat: Sidebar component (#205)
This commit is contained in:
@@ -1,135 +1,20 @@
|
||||
<template>
|
||||
<aside class="w-64" aria-label="Sidebar">
|
||||
<div class="overflow-y-auto py-4 px-3 bg-gray-50 rounded dark:bg-gray-800">
|
||||
<ul class="space-y-2">
|
||||
<li>
|
||||
<a href="#" class="flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<svg
|
||||
class="w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"></path>
|
||||
<path d="M12 2.252A8.014 8.014 0 0117.748 8H12V2.252z"></path>
|
||||
</svg>
|
||||
<span class="ml-3">Dashboard</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<svg
|
||||
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5 3a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2V5a2 2 0 00-2-2H5zM5 11a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2a2 2 0 00-2-2H5zM11 5a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V5zM11 13a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"
|
||||
></path>
|
||||
</svg>
|
||||
<span class="flex-1 ml-3 whitespace-nowrap">Kanban</span>
|
||||
<span class="inline-flex justify-center items-center px-2 ml-3 text-sm font-medium text-gray-800 bg-gray-200 rounded-full dark:bg-gray-700 dark:text-gray-300">Pro</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<svg
|
||||
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M8.707 7.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l2-2a1 1 0 00-1.414-1.414L11 7.586V3a1 1 0 10-2 0v4.586l-.293-.293z"></path>
|
||||
<path d="M3 5a2 2 0 012-2h1a1 1 0 010 2H5v7h2l1 2h4l1-2h2V5h-1a1 1 0 110-2h1a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V5z"></path>
|
||||
</svg>
|
||||
<span class="flex-1 ml-3 whitespace-nowrap">Inbox</span>
|
||||
<span class="inline-flex justify-center items-center p-3 ml-3 w-3 h-3 text-sm font-medium text-blue-600 bg-blue-200 rounded-full dark:bg-blue-900 dark:text-blue-200">3</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<svg
|
||||
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<span class="flex-1 ml-3 whitespace-nowrap">Users</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<svg
|
||||
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M10 2a4 4 0 00-4 4v1H5a1 1 0 00-.994.89l-1 9A1 1 0 004 18h12a1 1 0 00.994-1.11l-1-9A1 1 0 0015 7h-1V6a4 4 0 00-4-4zm2 5V6a2 2 0 10-4 0v1h4zm-6 3a1 1 0 112 0 1 1 0 01-2 0zm7-1a1 1 0 100 2 1 1 0 000-2z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
<span class="flex-1 ml-3 whitespace-nowrap">Products</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<svg
|
||||
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M3 3a1 1 0 00-1 1v12a1 1 0 102 0V4a1 1 0 00-1-1zm10.293 9.293a1 1 0 001.414 1.414l3-3a1 1 0 000-1.414l-3-3a1 1 0 10-1.414 1.414L14.586 9H7a1 1 0 100 2h7.586l-1.293 1.293z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
<span class="flex-1 ml-3 whitespace-nowrap">Sign In</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<svg
|
||||
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5 4a3 3 0 00-3 3v6a3 3 0 003 3h10a3 3 0 003-3V7a3 3 0 00-3-3H5zm-1 9v-1h5v2H5a1 1 0 01-1-1zm7 1h4a1 1 0 001-1v-1h-5v2zm0-4h5V8h-5v2zM9 8H4v2h5V8z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
<span class="flex-1 ml-3 whitespace-nowrap">Sign Up</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<aside v-bind="$attrs" :class="wrapperClasses" aria-label="Sidebar">
|
||||
<div class="h-full px-3 py-4 overflow-y-auto bg-gray-50 dark:bg-gray-800">
|
||||
<div class="space-y-2 font-medium">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineProps({
|
||||
children: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
collapseBehavior: {
|
||||
type: String, // 'collapse' | 'hide';
|
||||
default: 'collapse',
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
import { useAttrs } from 'vue'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
const wrapperClasses = twMerge('absolute top-0 left-0 z-40 w-64 h-screen transition-transform', attrs.class as string)
|
||||
</script>
|
||||
|
||||
38
src/components/Sidebar/SidebarCta.vue
Normal file
38
src/components/Sidebar/SidebarCta.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
label?: string
|
||||
}>(),
|
||||
{
|
||||
label: undefined,
|
||||
},
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'close'): void
|
||||
}>()
|
||||
|
||||
function close() {
|
||||
emit('close')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-4 mt-6 rounded-lg bg-blue-50 dark:bg-blue-900" role="alert">
|
||||
<div class="flex items-center mb-3">
|
||||
<span class="bg-orange-100 text-orange-800 text-sm font-semibold mr-2 px-2.5 py-0.5 rounded dark:bg-orange-200 dark:text-orange-900">{{ label }}</span>
|
||||
<button
|
||||
@click="close"
|
||||
type="button"
|
||||
class="ml-auto -mx-1.5 -my-1.5 bg-blue-50 inline-flex justify-center items-center w-6 h-6 text-blue-900 rounded-lg focus:ring-2 focus:ring-blue-400 p-1 hover:bg-blue-200 h-6 w-6 dark:bg-blue-900 dark:text-blue-400 dark:hover:bg-blue-800"
|
||||
aria-label="Close"
|
||||
>
|
||||
<span class="sr-only">Close</span>
|
||||
<svg class="w-2.5 h-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<slot name="default" />
|
||||
</div>
|
||||
</template>
|
||||
56
src/components/Sidebar/SidebarDropdownItem.vue
Normal file
56
src/components/Sidebar/SidebarDropdownItem.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
const isOpen = ref(false)
|
||||
function toggleDropdown() {
|
||||
isOpen.value = !isOpen.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="overflow-hidden">
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center w-full p-2 text-base text-gray-900 transition duration-75 rounded-lg group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700 z-10"
|
||||
aria-controls="dropdown-example"
|
||||
@click="toggleDropdown"
|
||||
>
|
||||
<slot name="icon">
|
||||
<svg
|
||||
class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 group-hover:text-gray-900 dark:text-gray-400 dark:group-hover:text-white"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 18 21"
|
||||
>
|
||||
<path
|
||||
d="M15 12a1 1 0 0 0 .962-.726l2-7A1 1 0 0 0 17 3H3.77L3.175.745A1 1 0 0 0 2.208 0H1a1 1 0 0 0 0 2h.438l.6 2.255v.019l2 7 .746 2.986A3 3 0 1 0 9 17a2.966 2.966 0 0 0-.184-1h2.368c-.118.32-.18.659-.184 1a3 3 0 1 0 3-3H6.78l-.5-2H15Z"
|
||||
/>
|
||||
</svg>
|
||||
</slot>
|
||||
<span class="flex-1 ml-3 text-left whitespace-nowrap">
|
||||
<slot name="trigger" />
|
||||
</span>
|
||||
<slot name="arrow-icon" :toggle-dropdown="toggleDropdown">
|
||||
<svg class="w-3 h-3 transition-all duration-300" :class="isOpen && 'rotate-180'" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4" />
|
||||
</svg>
|
||||
</slot>
|
||||
</button>
|
||||
|
||||
<div class="py-2 space-y-2 z-0 overflow-hidden">
|
||||
<transition
|
||||
:duration="150"
|
||||
enter-from-class="-translate-y-full"
|
||||
enter-to-class="translate-y-0"
|
||||
enter-active-class="transition duration-400 ease-out"
|
||||
leave-active-class="transition duration-400 ease-in"
|
||||
leave-from-class="translate-y-0"
|
||||
leave-to-class="-translate-y-full"
|
||||
>
|
||||
<div v-if="isOpen">
|
||||
<slot name="default" />
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
26
src/components/Sidebar/SidebarItem.vue
Normal file
26
src/components/Sidebar/SidebarItem.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { resolveComponent } from 'vue'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
link?: string
|
||||
tag?: string
|
||||
}>(),
|
||||
{
|
||||
link: '/',
|
||||
tag: 'router-link',
|
||||
},
|
||||
)
|
||||
|
||||
const component = props.tag === 'a' ? 'a' : resolveComponent(props.tag)
|
||||
const linkAttr = props.tag === 'a' ? 'href' : 'to'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="component" :[linkAttr]="link" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group">
|
||||
<slot name="icon" />
|
||||
<span class="flex-1 whitespace-nowrap" :class="$slots.icon && 'ml-3'">
|
||||
<slot name="default" />
|
||||
</span>
|
||||
<slot name="suffix" />
|
||||
</component>
|
||||
</template>
|
||||
17
src/components/Sidebar/SidebarItemGroup.vue
Normal file
17
src/components/Sidebar/SidebarItemGroup.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
const borderClasses = 'pt-4 mt-4 space-y-2 font-medium border-t border-gray-200 dark:border-gray-700'
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
border: boolean
|
||||
}>(),
|
||||
{
|
||||
border: false,
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="border && borderClasses">
|
||||
<slot name="default" />
|
||||
</div>
|
||||
</template>
|
||||
29
src/components/Sidebar/SidebarLogo.vue
Normal file
29
src/components/Sidebar/SidebarLogo.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { resolveComponent } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
name?: string
|
||||
link?: string
|
||||
logo?: string
|
||||
alt?: string
|
||||
tag?: string
|
||||
}>(),
|
||||
{
|
||||
name: '',
|
||||
link: '/',
|
||||
logo: '',
|
||||
tag: 'router-link',
|
||||
},
|
||||
)
|
||||
|
||||
const component = props.tag === 'a' ? 'a' : resolveComponent(props.tag)
|
||||
const linkAttr = props.tag === 'a' ? 'href' : 'to'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="component" :[linkAttr]="link" class="flex items-center mb-5 pl-2.5">
|
||||
<img :src="logo" class="h-6 mr-3 sm:h-7" :alt="alt ?? name" />
|
||||
<span class="self-center text-xl font-semibold whitespace-nowrap dark:text-white">{{ name }}</span>
|
||||
</component>
|
||||
</template>
|
||||
@@ -36,6 +36,11 @@ export { default as Pagination } from './components/Pagination/Pagination.vue'
|
||||
export { default as Progress } from './components/Progress/Progress.vue'
|
||||
export { default as Rating } from './components/Rating/Rating.vue'
|
||||
export { default as Sidebar } from './components/Sidebar/Sidebar.vue'
|
||||
export { default as SidebarCta } from './components/Sidebar/SidebarCta.vue'
|
||||
export { default as SidebarDropdownItem } from './components/Sidebar/SidebarDropdownItem.vue'
|
||||
export { default as SidebarItem } from './components/Sidebar/SidebarItem.vue'
|
||||
export { default as SidebarItemGroup } from './components/Sidebar/SidebarItemGroup.vue'
|
||||
export { default as SidebarLogo } from './components/Sidebar/SidebarLogo.vue'
|
||||
export { default as Table } from './components/Table/Table.vue'
|
||||
export { default as TableHead } from './components/Table/TableHead.vue'
|
||||
export { default as TableBody } from './components/Table/TableBody.vue'
|
||||
|
||||
Reference in New Issue
Block a user