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"