Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | 1x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 5x 5x 1x 1x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | <template>
<template v-if="!props.buttonsConfig?.length">
<button
:type="props.type || 'button'"
:class="buttonClasses"
:disabled="props.disabled"
:aria-label="computedAriaLabel ?? ''"
v-bind="restAttrs"
@click="props.handleClick"
>
<slot>
<template v-if="(props?.name ?? '').length > 0">
{{ $t(props.name || '') }}
</template>
</slot>
</button>
</template>
<template v-else>
<div :class="props.configCustomClass ?? 'flex gap-8'">
<button
v-for="(btn, i) in props.buttonsConfig"
:key="btn.id || btn.name || `btn-${i}`"
:type="btn.type || 'button'"
:class="getButtonClasses(btn)"
:disabled="btn.disabled ?? false"
:aria-label="getAriaLabel(btn)"
v-bind="getRestAttrs(btn)"
@click="btn.handleClick"
>
<template v-if="btn.name">
{{ $t(btn.name) }}
</template>
<template v-else-if="btn.children">
<component :is="btn.children" />
</template>
</button>
</div>
</template>
</template>
<script setup lang="ts">
import { isValidAttribute } from '@/shared/utils/safeAttributes';
import { clsx } from 'clsx';
import { computed, useAttrs } from 'vue';
import { useI18n } from 'vue-i18n';
import type { IButtonComponent } from './Button.model';
const props = defineProps<IButtonComponent>();
const restAttrs = useAttrs();
const { t } = useI18n();
const computedAriaLabelFallback = computed(() => ((props.name ?? '').length > 0 ? t(props.name || '') : 'Unnamed Button'));
const computedAriaLabel = computed(() => {
const hasLabelledBy = 'aria-labelledby' in restAttrs && !!restAttrs['aria-labelledby'];
return !hasLabelledBy ? String(restAttrs['aria-label'] ?? computedAriaLabelFallback.value) : '';
});
const getButtonClasses = (btn: IButtonComponent) => {
const variantClasses = {
primary: 'primary',
secondary: 'secondary',
tertiary: 'tertiary',
round: 'round rounded-full',
};
const sizeClasses = {
xs: btn.variant === 'round' ? 'text-sm h-6 w-6' : 'text-xs px-1 py-1',
sm: btn.variant === 'round' ? 'text-base h-8 w-8' : 'text-sm px-3 py-2',
lg: btn.variant === 'round' ? 'text-xl h-12 w-12' : 'text-base px-4 py-3',
};
return clsx([
'button-component',
variantClasses[btn.variant || 'primary'],
sizeClasses[btn.size || 'sm'],
btn.disabled ? 'bg-gray text-disabled cursor-not-allowed' : '',
typeof btn.className === 'string' ? btn.className : clsx(btn.className),
{ active: btn.active },
]);
};
const buttonClasses = computed(() => getButtonClasses(props));
const getAriaLabel = (btn: IButtonComponent) => {
const hasLabelledBy = 'aria-labelledby' in btn && !!btn['aria-labelledby'];
return !hasLabelledBy ? (btn['aria-label'] ?? (btn.name ? t(btn.name) : 'Unnamed Button')) : undefined;
};
const getRestAttrs = (btn: IButtonComponent) => {
const {
id,
key,
name,
type,
handleClick,
active,
className,
tooltip,
variant,
buttonsConfig,
configCustomClass,
size,
selected,
disabled,
children,
...rest
} = btn;
const safeAttrs: Record<string, any> = {};
for (const [k, v] of Object.entries(rest)) {
if (isValidAttribute(k)) {
safeAttrs[k] = v;
}
}
return safeAttrs;
};
</script>
|