primitives

Toggle Group

Toggle Group — базовый примитив RevueUI (Reka UI + shadcn-совместимый API).

Загрузка превью…

src/ui/toggle-group/ToggleGroup.vue

<!-- Radix `ToggleGroup` → Reka UI `ToggleGroupRoot`, тот же примитив. -->
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { computed, provide } from "vue"
import { ToggleGroupRoot, type ToggleGroupRootEmits, type ToggleGroupRootProps, useForwardPropsEmits } from "reka-ui"
import { cn } from "@/lib/utils"
import type { ToggleVariants } from "@/components/ui/toggle"
import { ToggleGroupContextKey } from "./context"

const props = withDefaults(
  defineProps<
    ToggleGroupRootProps & {
      /** Доп. классы. Имя `class` — соглашение shadcn-vue (не `className`). */
      class?: HTMLAttributes["class"]
      variant?: ToggleVariants["variant"]
      size?: ToggleVariants["size"]
      spacing?: number
      orientation?: "horizontal" | "vertical"
    }
  >(),
  {
    spacing: 2,
    orientation: "horizontal",
  }
)
const emits = defineEmits<ToggleGroupRootEmits>()

const delegatedProps = computed(() => {
  const { class: _class, variant: _variant, size: _size, spacing: _spacing, ...delegated } = props
  return delegated
})

const forwarded = useForwardPropsEmits(delegatedProps, emits)

provide(ToggleGroupContextKey, {
  variant: computed(() => props.variant),
  size: computed(() => props.size),
  spacing: computed(() => props.spacing),
  orientation: computed(() => props.orientation),
})
</script>

<template>
  <ToggleGroupRoot
    data-slot="toggle-group"
    :data-variant="props.variant"
    :data-size="props.size"
    :data-spacing="props.spacing"
    :data-orientation="props.orientation"
    :style="{ '--gap': props.spacing }"
    v-bind="forwarded"
    :class="
      cn(
        'cn-toggle-group group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-vertical:flex-col data-vertical:items-stretch',
        props.class
      )
    "
  >
    <slot />
  </ToggleGroupRoot>
</template>

src/ui/toggle-group/ToggleGroupItem.vue

<!-- Radix `ToggleGroupItem` → Reka UI `ToggleGroupItem`, тот же примитив. -->
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { computed } from "vue"
import { ToggleGroupItem as ToggleGroupItemPrimitive, type ToggleGroupItemProps } from "reka-ui"
import { cn } from "@/lib/utils"
import type { ToggleVariants } from "@/components/ui/toggle"
import { toggleVariants } from "@/components/ui/toggle"
import { useToggleGroupContext } from "./context"

const props = withDefaults(
  defineProps<
    ToggleGroupItemProps & {
      /** Доп. классы. Имя `class` — соглашение shadcn-vue (не `className`). */
      class?: HTMLAttributes["class"]
      variant?: ToggleVariants["variant"]
      size?: ToggleVariants["size"]
    }
  >(),
  {
    variant: "default",
    size: "default",
  }
)

const context = useToggleGroupContext()

const delegatedProps = computed(() => {
  const { class: _class, variant: _variant, size: _size, ...delegated } = props
  return delegated
})

const effectiveVariant = computed(() => context?.variant.value || props.variant)
const effectiveSize = computed(() => context?.size.value || props.size)
const effectiveSpacing = computed(() => context?.spacing.value)
</script>

<template>
  <ToggleGroupItemPrimitive
    data-slot="toggle-group-item"
    :data-variant="effectiveVariant"
    :data-size="effectiveSize"
    :data-spacing="effectiveSpacing"
    v-bind="delegatedProps"
    :class="
      cn(
        'cn-toggle-group-item shrink-0 focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t',
        toggleVariants({
          variant: effectiveVariant,
          size: effectiveSize,
        }),
        props.class
      )
    "
  >
    <slot />
  </ToggleGroupItemPrimitive>
</template>

src/ui/toggle-group/context.ts

import type { ComputedRef, InjectionKey } from "vue"
import { inject } from "vue"
import type { ToggleVariants } from "@/components/ui/toggle"

/**
 * Соответствие React `createContext`/`useContext` из toggle-group.tsx.
 * `ToggleGroup` кладёт сюда свои variant/size/spacing/orientation,
 * `ToggleGroupItem` читает их и падает обратно на собственные пропы,
 * если контекста нет (тот же порядок приоритета, что в оригинале:
 * `context.variant || variant`).
 */
export interface ToggleGroupContextValue {
  variant: ComputedRef<ToggleVariants["variant"] | undefined>
  size: ComputedRef<ToggleVariants["size"] | undefined>
  spacing: ComputedRef<number | undefined>
  orientation: ComputedRef<"horizontal" | "vertical" | undefined>
}

export const ToggleGroupContextKey: InjectionKey<ToggleGroupContextValue> =
  Symbol("ToggleGroupContext")

export function useToggleGroupContext(): ToggleGroupContextValue | null {
  return inject(ToggleGroupContextKey, null)
}

src/ui/toggle-group/index.ts

export { default as ToggleGroup } from "./ToggleGroup.vue"
export { default as ToggleGroupItem } from "./ToggleGroupItem.vue"

Установка

npx shadcn-vue@latest add https://revueui.rootapi.dev/r/toggle-group.json

Зависимости реестра

npm-зависимости

  • reka-ui

Источник: порт из ReUI (Keenthemes, MIT)