Instantly share code, notes, and snippets.
Created
March 3, 2021 13:24
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save productdevbook/1f43416056fdf152ffb3c561ea3fcded to your computer and use it in GitHub Desktop.
AppButton
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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