feat: Added footer component (#190)
* feat: Added footer component (#126) * add/prepare component files * make footer component basic * improve FooterCopyright component * add FooterLink component * add/add FooterBrand Component * add vertival align to FooterListGroup * add/add footer icon component * add/add FooterSitemapExample * add/FooterSitemapLinks remained part * add/add example --------- Co-authored-by: Ilya Artamonov <hire@ilya-artamonov.ru> * feat: Reworked footer component * docs: Updated components section in README.md --------- Co-authored-by: hirakei1203 <56023847+hirakei1203@users.noreply.github.com>
This commit is contained in:
@@ -1,42 +1,32 @@
|
||||
<script lang="ts" setup>
|
||||
import { useAttrs } from 'vue'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
type FooterType = 'sitemap' | 'default' | 'logo' | 'socialmedia'
|
||||
interface IFooterProps {
|
||||
sticky?: boolean
|
||||
footerType?: FooterType
|
||||
}
|
||||
const props = withDefaults(defineProps<IFooterProps>(), {
|
||||
sticky: false,
|
||||
footerType: 'default',
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
const wrapperClasses = twMerge(
|
||||
props.footerType === 'sitemap' && 'bg-gray-800',
|
||||
props.footerType === 'socialmedia' && 'p-4 bg-white sm:p-6 dark:bg-gray-800',
|
||||
props.footerType === 'logo' && 'p-4 bg-white rounded-lg shadow md:px-6 md:py-8 dark:bg-gray-800',
|
||||
props.footerType === 'default' && 'p-4 bg-white rounded-lg shadow md:flex md:items-center md:justify-between md:p-6 dark:bg-gray-800',
|
||||
props.sticky && 'absolute bottom-0 left-0 z-20 w-full border-t border-gray-200 dark:border-gray-600',
|
||||
attrs.class as string,
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<footer class="p-4 bg-white rounded-lg shadow md:flex md:items-center md:justify-between md:p-6 dark:bg-gray-800">
|
||||
<span class="text-sm text-gray-500 sm:text-center dark:text-gray-400">© 2022 <a href="https://flowbite.com/" class="hover:underline">Flowbite™</a>. All Rights Reserved.
|
||||
</span>
|
||||
<ul class="flex flex-wrap items-center mt-3 text-sm text-gray-500 dark:text-gray-400 sm:mt-0">
|
||||
<li>
|
||||
<a href="#" class="mr-4 hover:underline md:mr-6 ">About</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="mr-4 hover:underline md:mr-6">Privacy Policy</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="mr-4 hover:underline md:mr-6">Licensing</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="hover:underline">Contact</a>
|
||||
</li>
|
||||
</ul>
|
||||
<footer v-bind="$attrs" :class="wrapperClasses">
|
||||
<slot></slot>
|
||||
</footer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRefs } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
children: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
bgDark: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
container: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
40
src/components/Footer/FooterBrand.vue
Normal file
40
src/components/Footer/FooterBrand.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { useAttrs } from 'vue'
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
interface IFooterProps {
|
||||
href: string
|
||||
src: string
|
||||
alt?: string
|
||||
name: string
|
||||
imageClass?: string
|
||||
nameClass?: string
|
||||
aClass?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<IFooterProps>(), {
|
||||
href: '',
|
||||
src: '',
|
||||
alt: '',
|
||||
name: '',
|
||||
imageClass: '',
|
||||
nameClass: '',
|
||||
aClass: '',
|
||||
})
|
||||
|
||||
const wrapperClasses = twMerge('mb-6 md:mb-0', attrs.class as string)
|
||||
const aClasses = twMerge('flex items-center', props.aClass)
|
||||
const imageClasses = twMerge('h-8 mr-3', props.imageClass)
|
||||
const mameClasses = twMerge('self-center text-2xl font-semibold whitespace-nowrap dark:text-white', props.nameClass)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-bind="$attrs" :class="wrapperClasses">
|
||||
<a :href="href" :class="aClasses">
|
||||
<img :src="src" :class="imageClasses" :alt="alt" />
|
||||
<span :class="mameClasses">{{ name }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
34
src/components/Footer/FooterCopyright.vue
Normal file
34
src/components/Footer/FooterCopyright.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { useAttrs } from 'vue'
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
interface IFooterCopyrigthProps {
|
||||
year?: string | number
|
||||
by?: string
|
||||
href?: string
|
||||
aClass?: string
|
||||
copyrightMessage?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IFooterCopyrigthProps>(), {
|
||||
year: new Date().getFullYear(),
|
||||
by: '',
|
||||
href: '',
|
||||
aClass: '',
|
||||
copyrightMessage: 'All Rights Reserved.',
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
const spanClasses = twMerge('block text-sm text-gray-500 sm:text-center dark:text-gray-400', attrs.class as string)
|
||||
const aClasses = twMerge(props.href ? 'hover:underline' : 'ml-1', props.aClass)
|
||||
const byComponent = props.href ? 'a' : 'span'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-bind="$attrs" :class="spanClasses">
|
||||
© {{ year }}
|
||||
<component :is="byComponent" :href="href" :class="aClasses">{{ by }}</component>
|
||||
{{ copyrightMessage }}
|
||||
</span>
|
||||
</template>
|
||||
28
src/components/Footer/FooterIcon.vue
Normal file
28
src/components/Footer/FooterIcon.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { useAttrs } from 'vue'
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
interface IFooterIconProps {
|
||||
href?: string
|
||||
ariaLabel?: string
|
||||
srText?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<IFooterIconProps>(), {
|
||||
href: '',
|
||||
ariaLabel: '',
|
||||
srText: '',
|
||||
})
|
||||
const iconComponent = props.href ? 'a' : 'span'
|
||||
|
||||
const aClasses = twMerge('text-gray-500 hover:text-gray-900 dark:hover:text-white', attrs.class as string)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component v-bind="$attrs" :is="iconComponent" :href="href" :aria-label="ariaLabel" :class="aClasses">
|
||||
<slot />
|
||||
<span class="sr-only">{{ srText }}</span>
|
||||
</component>
|
||||
</template>
|
||||
30
src/components/Footer/FooterLink.vue
Normal file
30
src/components/Footer/FooterLink.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { resolveComponent, useAttrs } from 'vue'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
interface IFooterLinkProps {
|
||||
href: string
|
||||
aClass?: string
|
||||
component?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<IFooterLinkProps>(), {
|
||||
href: '',
|
||||
aClass: '',
|
||||
component: 'a',
|
||||
})
|
||||
const linkComponent = props.component === 'a' ? 'a' : resolveComponent(props.component)
|
||||
const linkAttr = props.component === 'router-link' ? 'to' : 'href'
|
||||
const aClasses = twMerge('hover:underline', props.aClass)
|
||||
const liClasses = twMerge('mr-4 md:mr-6 last:mr-0', attrs.class as string)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li v-bind="$attrs" :class="liClasses">
|
||||
<component :is="linkComponent" :[linkAttr]="href" :class="aClasses">
|
||||
<slot />
|
||||
</component>
|
||||
</li>
|
||||
</template>
|
||||
15
src/components/Footer/FooterLinkGroup.vue
Normal file
15
src/components/Footer/FooterLinkGroup.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script lang="ts" setup>
|
||||
import { useAttrs } from 'vue'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
const wrapperClasses = twMerge('flex flex-wrap items-center mt-3 text-sm font-medium text-gray-500 dark:text-gray-400 sm:mt-0', attrs.class as string)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul v-bind="$attrs" :class="wrapperClasses">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</template>
|
||||
@@ -20,6 +20,11 @@ export { default as Badge } from './components/Badge/Badge.vue'
|
||||
export { default as TheCard } from './components/Card/TheCard.vue'
|
||||
export { default as Carousel } from './components/Carousel/Carousel.vue'
|
||||
export { default as Footer } from './components/Footer/Footer.vue'
|
||||
export { default as FooterBrand } from './components/Footer/FooterBrand.vue'
|
||||
export { default as FooterCopyright } from './components/Footer/FooterCopyright.vue'
|
||||
export { default as FooterLink } from './components/Footer/FooterLink.vue'
|
||||
export { default as FooterLinkGroup } from './components/Footer/FooterLinkGroup.vue'
|
||||
export { default as FooterIcon } from './components/Footer/FooterIcon.vue'
|
||||
export { default as ListGroup } from './components/ListGroup/ListGroup.vue'
|
||||
export { default as ListGroupItem } from './components/ListGroup/components/ListGroupItem/ListGroupItem.vue'
|
||||
export { default as Modal } from './components/Modal/Modal.vue'
|
||||
|
||||
Reference in New Issue
Block a user