๐ŸŸข Indicator


Basic

NIndicator is a component that can be used to display a basic indicator with or without a label.

PR
<template>
  <NIndicator>
    <NAvatar label="PR" />
  </NIndicator>
</template>

Variant

indicator="{variant}" - change the variant of the button.

For the moment, only the solid variant is available. You can add your own variant through configuration.
VariantDescription
solidThe default variant.
~The unstyle or base variant

Color

indicator="{variant}-{color}" - change the color of the variant.

You can use any color provided by the Tailwind CSS color palette, the default is primary. You can also add your own colors to the palette through the Configuration section.
PR
<template>
  <div flex="~ items-center" gap-4>
    <NIndicator indicator="solid-gray">
      <NAvatar src="/images/avatar.png" />
    </NIndicator>

    <NIndicator indicator="solid-lime">
      <NAvatar label="PR" />
    </NIndicator>

    <NIndicator indicator="solid-indigo">
      <NAvatar />
    </NIndicator>
  </div>
</template>

Label

label="{label}" - add label to the indicator.

11
NewNew
PR1 message1 message
<template>
  <div flex="~ items-center" gap-x-6>
    <NIndicator indicator="solid-red" label="1">
      <NButton label="i-heroicons-bell group-active:i-heroicons-bell-solid" size="lg" icon btn="soft-gray square" class="group rounded-full" />
    </NIndicator>

    <NIndicator indicator="solid-primary" label="New">
      <NButton label="i-heroicons-envelope group-active:i-heroicons-envelope-solid" size="xl" icon btn="text-gray" group class="group rounded-full p-1.5" />
    </NIndicator>

    <NIndicator indicator="solid-info" label="1 message">
      <NAvatar src="https://avatars.githubusercontent.com/u/33350692?v=4" alt="Phojie Rengel" />
    </NIndicator>
  </div>
</template>

Ping

ping - add a ping animation to the indicator.

PR
<template>
  <NIndicator indicator="solid-red" ping>
    <NAvatar label="PR" />
  </NIndicator>
</template>

Size

indicator="{size}" - change the size of the indicator.

PR
PR
<template>
  <div flex="~ items-center" gap-4>
    <NIndicator indicator="solid-success">
      <NAvatar src="/images/avatar.png" alt="Phojie Rengel" />
    </NIndicator>

    <NIndicator size="2xl" indicator="solid-info">
      <NAvatar src="/images/avatar.png" alt="Phojie Rengel" />
    </NIndicator>
  </div>
</template>

Visibility

visible - change the visibility of the indicator.

11
<script setup lang="ts">
const isVisible = ref(true)
</script>

<template>
  <div flex="~" gap-4>
    <NIndicator indicator="solid-red" label="1" size="lg" :visible="isVisible">
      <NButton label="i-heroicons-bell group-active:i-heroicons-bell-solid" size="lg" icon btn="soft-gray square" class="group rounded-full" />
    </NIndicator>

    <NSwitch
      v-model="isVisible"
    />
  </div>
</template>

Placement

indicator="{placement}" - change the placement of the indicator.

PlacementDescription
top-rightThe default placement.
top-leftPosition indicator top left.
bottom-rightPosition indicator bottom right.
bottom-leftPosition indicator bottom left.
You can add your own placement through configuration. see Configuration section.
TR
BR
TL
BL
<template>
  <div flex="~ items-center" gap-4>
    <NIndicator indicator="top-right solid-error" ping>
      <NAvatar label="TR" />
    </NIndicator>

    <NIndicator indicator="bottom-right solid-info" ping>
      <NAvatar label="BR" />
    </NIndicator>

    <NIndicator indicator="top-left solid-warning" ping>
      <NAvatar label="TL" />
    </NIndicator>

    <NIndicator indicator="bottom-left solid-success" ping>
      <NAvatar label="BL" />
    </NIndicator>
  </div>
</template>

Slots

NameDescription
defaultThe content of the indicator.
indicatorThe entire indicator, this will reset including the position.
labelThe label of the indicator.

Props

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

  }
}

Presets

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,
]

Component

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

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>