Indicator

Displays an indicator that can be used to show the status of a task or a component.

Examples

Basic

Preview
Code
PR

Label

PropDefaultTypeDescription
label-stringThe label of the indicator.
Preview
Code
11
NewNew
PR1 message1 message

Variant

PropDefaultTypeDescription
indicatorsolid{variant}The variant of the indicator.
VariantDescription
solidThe default variant.
~The unstyle or base variant

Color

PropDefaultTypeDescription
indicator{variant}-primary{variant}-{color}The color of the indicator.
Preview
Code
PR

Ping

PropDefaultTypeDescription
pingfalsebooleanEnable the ping animation.
Preview
Code
PR

Size

PropDefaultTypeDescription
sizemdstringThe size of the indicator.
Preview
Code
PR
PR

Visibility

PropDefaultTypeDescription
visibletruebooleanShow or hide the indicator.
Preview
Code
11

Placement

PropDefaultTypeDescription
indicatortop-right{placement}The position of the indicator.
PlacementDescription
top-rightPosition indicator top right.
top-leftPosition indicator top left.
bottom-rightPosition indicator bottom right.
bottom-leftPosition indicator bottom left.
Preview
Code
TR
BR
TL
BL

Slots

NamePropsDescription
default-The content of the indicator.
indicator-The entire indicator, this will reset including the position.
label-The label of the indicator.

Presets

shortcuts/indicator.ts
type IndicatorPrefix = 'indicator'

export const staticIndicator: Record<`${IndicatorPrefix}-${string}` | IndicatorPrefix, string> = {
  // config
  'indicator-default-variant': 'indicator-solid',
  'indicator-default-placement': 'indicator-top-right',

  // base
  'indicator': 'absolute min-h-1.5em min-w-1.5em flex items-center justify-center rounded-full font-medium py-none px-0.3em ring-2 ring-$c-background',

  // indicator type sizes
  'indicator-dot': 'size-0.45em',
  'indicator-label': 'size-0.75em',

  // wrapper
  'indicator-wrapper': 'relative inline-flex',

  // placements
  'indicator-top-right': 'top-0 -ml-1.3em -mt-0.1em',
  'indicator-bottom-right': 'bottom-0 -ml-1.3em -mb-0.1em',
  'indicator-top-left': 'top-0 left-0 -mr-1.3em -mt-0.1em',
  'indicator-bottom-left': 'bottom-0 left-0 -mr-1.3em -mb-0.1em',
}

export const dynamicIndicator = [
  [/^indicator-solid(-(\S+))?$/, ([, , c = 'primary']) => `bg-${c}-600 dark:bg-${c}-500 text-inverted`],
]

export const indicator = [
  ...dynamicIndicator,
  staticIndicator,
]

Props

types/indicator.ts
export interface NIndicatorProps {
  /**
   * Set the size of the indicator.
   *
   * @default 'md'
   */
  size?: string
  /**
   * Allows you to add `UnaUI` button preset properties,
   * Think of it as a shortcut for adding options or variants to the preset if available.
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/indicator.ts
   * @example
   * indicator="solid-green top-right"
   */
  indicator?: string
  /**
   * Add a label to the indicator.
   *
   * @example
   * label="new"
   */
  label?: string

  /**
   * Add ping animation to the indicator.
   *
   * @default false
   */
  ping?: boolean
  /**
   * Set visibility of the indicator.
   *
   * @default true
   */
  visible?: boolean

  /**
   * `UnaUI` preset configuration
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/indicator.ts
   */
  una?: {
    // base
    indicator?: string

    // wrapper
    indicatorWrapper?: string

  }
}

Components

Indicator.vue
<script setup lang="ts">
import type { NIndicatorProps } from '../../types'
import { createReusableTemplate } from '@vueuse/core'
import { computed } from 'vue'

defineOptions({
  inheritAttrs: false,
})

const props = withDefaults(defineProps<NIndicatorProps>(), {
  visible: true,
})

const indicatorPlacements = ['top-left', 'top-right', 'bottom-left', 'bottom-right'] as const
const hasPlacement = computed(() => indicatorPlacements.some(indicatorPlacements => props.indicator?.includes(indicatorPlacements)))

const indicatorVariants = ['solid'] as const
const hasVariant = computed(() => indicatorVariants.some(indicatorVariants => props.indicator?.includes(indicatorVariants)))

const isBaseVariant = computed(() => props.indicator?.includes('~'))

const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{
  ping?: boolean
}>()
</script>

<template>
  <div
    indicator="wrapper"
    :class="una?.indicatorWrapper"
  >
    <slot />

    <span v-if="visible" :size="size">
      <DefineTemplate v-slot="{ ping }">
        <slot name="indicator">
          <span
            v-bind="$attrs"
            :indicator="indicator"
            class="indicator whitespace-nowrap"
            :class="[
              { 'indicator-default-placement': !hasPlacement },
              { 'indicator-default-variant': !hasVariant && !isBaseVariant },
              { 'animate-ping ring-none': ping },
              !label ? 'indicator-dot' : 'indicator-label',
              una?.indicator,
            ]"
          >
            <slot name="label">
              {{ label }}
            </slot>
          </span>
        </slot>
      </DefineTemplate>

      <ReuseTemplate :ping="ping" />
      <ReuseTemplate />
    </span>
  </div>
</template>