Skip to content

Instantly share code, notes, and snippets.

@productdevbook
Created March 3, 2021 13:24
Show Gist options
  • Save productdevbook/1f43416056fdf152ffb3c561ea3fcded to your computer and use it in GitHub Desktop.
Save productdevbook/1f43416056fdf152ffb3c561ea3fcded to your computer and use it in GitHub Desktop.
AppButton
<template>
<button
class="focus:outline-none inline-flex relative items-center font-medium transition duration-150 ease-in-out border"
:disabled="disabled"
:class="[colorObj, curserObj, centerObj, sizeObj, textObj]"
v-bind="$attrs"
v-on="$listeners"
>
<template v-if="loading">
<svg
class="animate-spin w-5 h-5 mr-3 -ml-1"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
</template>
<template v-if="iconDirection === 'leading'">
<component :is="icon" :class="[iconDirectionObj, animationObj]" />
</template>
<slot />
<template v-if="icon && !iconDirection">
<component :is="icon" :class="[iconObj, animationObj]" />
</template>
<template v-if="iconDirection === 'trailing'">
<component :is="icon" :class="[iconDirectionObj, animationObj]" />
</template>
<template v-if="notification">
<span class="absolute top-0 right-0 flex w-3 h-3 -mt-1 -mr-1">
<span
class="animate-ping absolute inline-flex w-full h-full bg-pink-400 rounded-full opacity-75"
/>
<span class="relative inline-flex w-3 h-3 bg-pink-500 rounded-full" />
</span>
</template>
<span v-if="target" class="absolute top-0 right-0 -mr-1 -mt-1 z-10">
<span class="flex w-3 h-3 relative"
><span
class="animate-ping absolute inline-flex h-full w-full rounded-full opacity-75 bg-green-400" /><span
class="relative inline-flex rounded-full h-3 w-3 bg-green-500" /></span
></span>
</button>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from '@nuxtjs/composition-api'
import { AnimationStyle, ColorStyle, SizeStyle, Direction } from '~/types/components/button'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const icons = require('@vue-hero-icons/outline')
// https://vue-hero-icons.netlify.app/ icons sets
export default defineComponent({
components: { ...icons },
props: {
iconDirection: {
type: String as PropType<Direction>,
required: false,
default: '',
validator: (value: string) => {
const validIconDirection = ['trailing', 'leading']
return validIconDirection.includes(value)
},
},
target: {
type: Boolean,
default: false,
required: false,
},
icon: {
type: String,
required: false,
default: '',
},
animation: {
type: String as PropType<AnimationStyle>,
required: false,
default: null,
validator: (value: string) => {
const validAnimation = ['spin', 'bounce', 'ping']
return validAnimation.includes(value)
},
},
size: {
type: String as PropType<SizeStyle>,
required: false,
default: 'base',
validator: (value: string) => {
const validSize = ['xs', 'sm', 'base', 'lg', 'xl']
return validSize.includes(value)
},
},
color: {
type: String as PropType<ColorStyle>,
required: false,
default: 'primary',
validator: (value: string) => {
const validColor = ['primary', 'black', 'red', 'secondary', 'white']
return validColor.includes(value)
},
},
notification: {
type: Boolean,
required: false,
},
loading: {
type: Boolean,
required: false,
default: false,
},
disabled: {
type: Boolean,
required: false,
default: false,
},
center: {
type: Boolean,
required: false,
default: false,
},
},
setup(props) {
const centerObj = computed(() => {
if (props.center) {
return 'flex justify-center'
}
})
const curserObj = computed(() => {
if (props.loading || props.disabled) {
return 'cursor-not-allowed'
}
})
const iconDirectionObj = computed(() => {
const sizes = {
xs: 'ml-0.5 mr-2 h-4 w-4',
sm: 'ml-0.5 mr-2 h-4 w-4',
base: '-ml-1 mr-2 h-5 w-5',
lg: '-ml-1 mr-3 h-5 w-5',
xl: '-ml-1 mr-3 h-5 w-5',
}
const sizesTraling = {
xs: 'ml-2 -mr-0.5 h-4 w-4',
sm: 'ml-2 -mr-0.5 h-4 w-4',
base: 'ml-2 -mr-1 h-5 w-5',
lg: 'ml-3 -mr-1 h-5 w-5',
xl: 'ml-3 -mr-1 h-5 w-5',
}
if (props.iconDirection === 'leading') {
return sizes[props.size]
} else {
return sizesTraling[props.size]
}
})
const iconObj = computed(() => {
const sizes = {
xs: '-mx-1 -my-0.5',
sm: '-mx-1 -my-0.5',
base: '-mx-1 -my-0.5',
lg: '-mx-1',
xl: '-mx-1',
}
return sizes[props.size]
})
const colorObj = computed(() => {
const sizes = {
primary:
'text-green-50 bg-green-700 border-transparent hover:bg-green-700 focus:border-green-700 focus:shadow-outline-green active:bg-green-700',
black:
'text-gray-900 bg-gray-200 hover:bg-gray-300 border-transparent focus:border-gray-700 focus:shadow-outline-gray active:bg-gray-700',
red:
'ext-white bg-red-600 border-transparent hover:bg-red-500 focus:border-red-700 focus:shadow-outline-red active:bg-red-700',
secondary:
'text-green-700 bg-green-50 border-transparent hover:bg-green-200 focus:border-green-300 focus:shadow-outline-green active:bg-green-200',
white:
'text-gray-700 bg-white border-gray-300 hover:text-gray-500 hover:bg-gray-100 focus:border-green-300 focus:shadow-outline-green active:text-gray-800 active:bg-gray-50',
}
return sizes[props.color]
})
const sizeObj = computed(() => {
const sizes = {
xs: 'px-2.5 py-1.5 leading-4',
sm: 'px-3 py-2 leading-4',
base: 'px-4 py-2 leading-5',
lg: 'px-4 py-2 leading-6',
xl: 'px-6 py-3 leading-6',
}
return sizes[props.size]
})
const textObj = computed(() => {
const sizes = {
xs: 'rounded text-xs',
sm: 'rounded-lg text-sm',
base: 'rounded-lg text-sm',
lg: 'rounded-lg text-base',
xl: 'rounded-lg text-base',
}
return sizes[props.size]
})
const animationObj = computed(() => {
const animations = {
spin: 'animate-spin',
bounce: 'animate-bounce',
ping: 'animate-ping',
}
return animations[props.animation]
})
return {
centerObj,
curserObj,
iconDirectionObj,
colorObj,
sizeObj,
textObj,
animationObj,
iconObj,
}
},
})
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment