feat: add ToggleGroup support for labeled boolean widgets

This commit is contained in:
Csongor Czezar
2025-12-30 14:52:38 -08:00
parent 795962f3c3
commit 035a0f250c
10 changed files with 411 additions and 118 deletions

View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
import { reactiveOmit } from '@vueuse/core'
import type { VariantProps } from 'class-variance-authority'
import type { ToggleGroupRootEmits, ToggleGroupRootProps } from 'reka-ui'
import { ToggleGroupRoot, useForwardPropsEmits } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { provide } from 'vue'
import type { toggleVariants } from '@/components/ui/toggle'
import { cn } from '@/utils/tailwindUtil'
type ToggleGroupVariants = VariantProps<typeof toggleVariants>
const props = defineProps<
ToggleGroupRootProps & {
class?: HTMLAttributes['class']
variant?: ToggleGroupVariants['variant']
size?: ToggleGroupVariants['size']
}
>()
const emits = defineEmits<ToggleGroupRootEmits>()
provide('toggleGroup', {
variant: props.variant,
size: props.size
})
const delegatedProps = reactiveOmit(props, 'class')
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<ToggleGroupRoot
v-slot="slotProps"
v-bind="forwarded"
:class="cn('flex items-center justify-center gap-1', props.class)"
>
<slot v-bind="slotProps" />
</ToggleGroupRoot>
</template>

View File

@@ -0,0 +1,45 @@
<script setup lang="ts">
import { reactiveOmit } from '@vueuse/core'
import type { VariantProps } from 'class-variance-authority'
import type { ToggleGroupItemProps } from 'reka-ui'
import { ToggleGroupItem, useForwardProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { inject } from 'vue'
import { toggleVariants } from '@/components/ui/toggle'
import { cn } from '@/utils/tailwindUtil'
type ToggleGroupVariants = VariantProps<typeof toggleVariants>
const props = defineProps<
ToggleGroupItemProps & {
class?: HTMLAttributes['class']
variant?: ToggleGroupVariants['variant']
size?: ToggleGroupVariants['size']
}
>()
const context = inject<ToggleGroupVariants>('toggleGroup')
const delegatedProps = reactiveOmit(props, 'class', 'size', 'variant')
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<ToggleGroupItem
v-slot="slotProps"
v-bind="forwardedProps"
:class="
cn(
toggleVariants({
variant: context?.variant || variant,
size: context?.size || size
}),
props.class
)
"
>
<slot v-bind="slotProps" />
</ToggleGroupItem>
</template>

View File

@@ -0,0 +1,2 @@
export { default as ToggleGroup } from "./ToggleGroup.vue"
export { default as ToggleGroupItem } from "./ToggleGroupItem.vue"

View File

@@ -0,0 +1,42 @@
<script setup lang="ts">
import { reactiveOmit } from '@vueuse/core'
import type { ToggleEmits, ToggleProps } from 'reka-ui'
import { Toggle, useForwardPropsEmits } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { cn } from '@/utils/tailwindUtil'
import type { ToggleVariants } from '.'
import { toggleVariants } from '.'
const props = withDefaults(
defineProps<
ToggleProps & {
class?: HTMLAttributes['class']
variant?: ToggleVariants['variant']
size?: ToggleVariants['size']
}
>(),
{
variant: 'default',
size: 'default',
disabled: false
}
)
const emits = defineEmits<ToggleEmits>()
const delegatedProps = reactiveOmit(props, 'class', 'size', 'variant')
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<Toggle
v-slot="slotProps"
data-slot="toggle"
v-bind="forwarded"
:class="cn(toggleVariants({ variant, size }), props.class)"
>
<slot v-bind="slotProps" />
</Toggle>
</template>

View File

@@ -0,0 +1,28 @@
import type { VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
export { default as Toggle } from "./Toggle.vue"
export const toggleVariants = cva(
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
{
variants: {
variant: {
default: "bg-transparent",
outline:
"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
},
size: {
default: "h-9 px-2 min-w-9",
sm: "h-8 px-1.5 min-w-8",
lg: "h-10 px-2.5 min-w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
)
export type ToggleVariants = VariantProps<typeof toggleVariants>