feat: Sidebar component (#205)

This commit is contained in:
Ilya Artamonov
2023-09-25 16:53:19 +03:00
committed by GitHub
parent d7910757ed
commit 5564c3a449
15 changed files with 1586 additions and 141 deletions

View File

@@ -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>

View 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>

View 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>

View 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>

View 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>

View 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>

View File

@@ -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'