Spaces:
Sleeping
Sleeping
| <template> | |
| <span :class="badgeClasses" v-bind="$attrs"> | |
| <!-- Dot indicator --> | |
| <span v-if="dot" class="saap-badge__dot" aria-hidden="true" /> | |
| <!-- Icon --> | |
| <component | |
| v-if="icon" | |
| :is="iconComponent" | |
| class="saap-badge__icon" | |
| aria-hidden="true" | |
| /> | |
| <!-- Content --> | |
| <span class="saap-badge__content"> | |
| <slot /> | |
| </span> | |
| </span> | |
| </template> | |
| <script setup lang="ts"> | |
| import { computed, defineProps, withDefaults } from 'vue' | |
| import type { BadgeProps } from '@/types' | |
| interface Props extends BadgeProps { | |
| icon?: string | |
| } | |
| const props = withDefaults(defineProps<Props>(), { | |
| variant: 'neutral', | |
| size: 'md', | |
| dot: false, | |
| outline: false | |
| }) | |
| // Computed | |
| const badgeClasses = computed(() => [ | |
| 'saap-badge', | |
| `saap-badge--${props.variant}`, | |
| `saap-badge--${props.size}`, | |
| { | |
| 'saap-badge--outline': props.outline, | |
| 'saap-badge--with-dot': props.dot, | |
| 'saap-badge--with-icon': props.icon | |
| } | |
| ]) | |
| const iconComponent = computed(() => { | |
| if (props.icon) { | |
| return resolveIconComponent(props.icon) | |
| } | |
| return null | |
| }) | |
| // Methods | |
| function resolveIconComponent(iconName: string) { | |
| try { | |
| return () => import(`lucide-vue-next/dist/esm/icons/${iconName}.js`) | |
| } catch { | |
| return null | |
| } | |
| } | |
| </script> | |
| <style lang="scss" scoped> | |
| @import '@/styles/mixins.scss'; | |
| .saap-badge { | |
| @include badge-base; | |
| // Variants - Solid | |
| &--success:not(&--outline) { | |
| @include badge-success; | |
| } | |
| &--warning:not(&--outline) { | |
| @include badge-warning; | |
| } | |
| &--error:not(&--outline) { | |
| @include badge-error; | |
| } | |
| &--info:not(&--outline) { | |
| @include badge-info; | |
| } | |
| &--neutral:not(&--outline) { | |
| background: var(--saap-neutral-200); | |
| color: var(--saap-neutral-800); | |
| } | |
| // Variants - Outline | |
| &--success#{&--outline} { | |
| background: transparent; | |
| color: var(--saap-success); | |
| border: 1px solid var(--saap-success); | |
| } | |
| &--warning#{&--outline} { | |
| background: transparent; | |
| color: var(--saap-warning); | |
| border: 1px solid var(--saap-warning); | |
| } | |
| &--error#{&--outline} { | |
| background: transparent; | |
| color: var(--saap-error); | |
| border: 1px solid var(--saap-error); | |
| } | |
| &--info#{&--outline} { | |
| background: transparent; | |
| color: var(--saap-info); | |
| border: 1px solid var(--saap-info); | |
| } | |
| &--neutral#{&--outline} { | |
| background: transparent; | |
| color: var(--saap-neutral-600); | |
| border: 1px solid var(--saap-neutral-300); | |
| } | |
| // Sizes | |
| &--sm { | |
| padding: var(--saap-space-1) var(--saap-space-2); | |
| font-size: 0.625rem; // 10px | |
| gap: var(--saap-space-1); | |
| } | |
| &--md { | |
| padding: var(--saap-space-1) var(--saap-space-3); | |
| font-size: var(--saap-text-xs); | |
| gap: var(--saap-space-1); | |
| } | |
| &--lg { | |
| padding: var(--saap-space-2) var(--saap-space-4); | |
| font-size: var(--saap-text-sm); | |
| gap: var(--saap-space-2); | |
| } | |
| } | |
| .saap-badge__dot { | |
| width: 0.375rem; // 6px | |
| height: 0.375rem; | |
| border-radius: 50%; | |
| background: currentColor; | |
| flex-shrink: 0; | |
| .saap-badge--sm & { | |
| width: 0.25rem; // 4px | |
| height: 0.25rem; | |
| } | |
| .saap-badge--lg & { | |
| width: 0.5rem; // 8px | |
| height: 0.5rem; | |
| } | |
| } | |
| .saap-badge__icon { | |
| width: 0.75rem; // 12px | |
| height: 0.75rem; | |
| flex-shrink: 0; | |
| .saap-badge--sm & { | |
| width: 0.625rem; // 10px | |
| height: 0.625rem; | |
| } | |
| .saap-badge--lg & { | |
| width: 1rem; // 16px | |
| height: 1rem; | |
| } | |
| } | |
| .saap-badge__content { | |
| line-height: 1; | |
| } | |
| // Special agent status badges | |
| .saap-badge { | |
| &--agent-active { | |
| background: var(--saap-success); | |
| color: white; | |
| } | |
| &--agent-inactive { | |
| background: var(--saap-neutral-400); | |
| color: white; | |
| } | |
| &--agent-processing { | |
| background: var(--saap-warning); | |
| color: white; | |
| &::after { | |
| content: ''; | |
| display: inline-block; | |
| width: 0.5rem; | |
| height: 0.5rem; | |
| margin-left: var(--saap-space-1); | |
| border: 1px solid currentColor; | |
| border-top-color: transparent; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| } | |
| &--agent-error { | |
| background: var(--saap-error); | |
| color: white; | |
| } | |
| &--agent-idle { | |
| background: var(--saap-neutral-500); | |
| color: white; | |
| } | |
| } | |
| @keyframes spin { | |
| to { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| </style> | |