feat: rework BaseButton

This commit is contained in:
Dominik Pschenitschni 2022-10-17 13:26:20 +02:00
parent a2c1702eef
commit e8c6afce72
Signed by: dpschen
GPG Key ID: B257AC0149F43A77

View File

@ -1,18 +1,52 @@
<!-- a disabled link of any kind is not a link -->
<!-- we have a router link -->
<!-- just a normal link -->
<!-- a button it shall be -->
<!-- note that we only pass the click listener here -->
<template> <template>
<component <div
:is="componentNodeName" v-if="disabled === true && (to !== undefined || href !== undefined)"
class="base-button" class="base-button"
:class="{ 'base-button--type-button': isButton }" :aria-disabled="disabled || undefined"
v-bind="elementBindings"
:disabled="disabled || undefined"
ref="button" ref="button"
> >
<slot/> <slot/>
</component> </div>
<router-link
v-else-if="to !== undefined"
:to="to"
class="base-button"
ref="button"
>
<slot/>
</router-link>
<a v-else-if="href !== undefined"
class="base-button"
rel="noreferrer noopener nofollow"
target="_blank"
ref="button"
>
<slot/>
</a>
<button
v-else
:type="type"
class="base-button base-button--type-button"
:disabled="disabled || undefined"
ref="button"
@click="(event: MouseEvent) => emit('click', event)"
>
<slot/>
</button>
</template> </template>
<script lang="ts"> <script lang="ts">
export default { inheritAttrs: false } const BASE_BUTTON_TYPES_MAP = {
BUTTON: 'button',
SUBMIT: 'submit',
} as const
export type BaseButtonTypes = typeof BASE_BUTTON_TYPES_MAP[keyof typeof BASE_BUTTON_TYPES_MAP] | undefined
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
@ -20,77 +54,36 @@ export default { inheritAttrs: false }
// by doing so we make it easy abstract the functionality from style and enable easier and semantic // by doing so we make it easy abstract the functionality from style and enable easier and semantic
// correct button and link usage. Also see: https://css-tricks.com/a-complete-guide-to-links-and-buttons/#accessibility-considerations // correct button and link usage. Also see: https://css-tricks.com/a-complete-guide-to-links-and-buttons/#accessibility-considerations
// the component tries to heuristically determine what it should be checking the props (see the // the component tries to heuristically determine what it should be checking the props
// componentNodeName and elementBindings ref for this).
// NOTE: Do NOT use buttons with @click to push routes. => Use router-links instead! // NOTE: Do NOT use buttons with @click to push routes. => Use router-links instead!
import { ref, watchEffect, computed, useAttrs, type PropType } from 'vue' import {unrefElement} from '@vueuse/core'
import {ref, type HTMLAttributes} from 'vue'
import type {RouteLocationNamedRaw} from 'vue-router'
const BASE_BUTTON_TYPES_MAP = Object.freeze({ export interface BaseButtonProps extends HTMLAttributes {
button: 'button', type?: BaseButtonTypes
submit: 'submit', disabled?: boolean
}) to?: RouteLocationNamedRaw
href?: string
type BaseButtonTypes = keyof typeof BASE_BUTTON_TYPES_MAP
const props = defineProps({
type: {
type: String as PropType<BaseButtonTypes>,
default: 'button',
},
disabled: {
type: Boolean,
default: false,
},
})
const componentNodeName = ref<Node['nodeName']>('button')
interface ElementBindings {
type?: string;
rel?: string;
target?: string;
} }
const elementBindings = ref({}) export interface BaseButtonEmits {
(e: 'click', payload: MouseEvent): void
}
const attrs = useAttrs() const {
watchEffect(() => { type = BASE_BUTTON_TYPES_MAP.BUTTON,
// by default this component is a button element with the attribute of the type "button" (default prop value) disabled = false,
let nodeName = 'button' } = defineProps<BaseButtonProps>()
let bindings: ElementBindings = {type: props.type}
// if we find a "to" prop we set it as router-link const emit = defineEmits<BaseButtonEmits>()
if ('to' in attrs) {
nodeName = 'router-link'
bindings = {}
}
// if there is a href we assume the user wants an external link via a link element const button = ref<HTMLElement | null>(null)
// we also set a predefined value for the attribute rel, but make it possible to overwrite this by the user.
if ('href' in attrs) {
nodeName = 'a'
bindings = {
rel: 'noreferrer noopener nofollow',
target: '_blank',
}
}
componentNodeName.value = nodeName
elementBindings.value = {
...bindings,
...attrs,
}
})
const isButton = computed(() => componentNodeName.value === 'button')
const button = ref()
function focus() { function focus() {
button.value.focus() unrefElement(button)?.focus()
} }
defineExpose({ defineExpose({