Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor useSingleOrMultiple #693

Merged
merged 12 commits into from
Mar 4, 2024
4 changes: 2 additions & 2 deletions docs/content/components/popover.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Import all parts and piece them together.

```vue
<script setup>
import { PopoverArrow, PopoverAnchor, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
import { PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>

<template>
Expand Down Expand Up @@ -294,7 +294,7 @@ You can anchor the content to another element if you do not want to use the trig
```vue line=8-12
// index.vue
<script setup>
import { PopoverArrow, PopoverAnchor, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
import { PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>

<template>
Expand Down
8 changes: 4 additions & 4 deletions docs/content/meta/AccordionRoot.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
{
'name': 'defaultValue',
'description': '<p>The default value of the item to expand when type is &quot;single&quot; or the default values of the items to expand when type is &quot;multiple&quot;.\nUse when you do not need to control the state of the item(s).</p>\n',
'description': '<p>The default active value of the item(s).</p>\n<p>Use when you do not need to control the state of the item(s).</p>\n',
'type': 'string | string[]',
'required': false
},
Expand All @@ -42,7 +42,7 @@
},
{
'name': 'modelValue',
'description': '<p>The controlled value of the item to expand when type is &quot;single&quot; or the controlled values of the items to expand when type is &quot;multiple&quot;.</p>\n',
'description': '<p>The controlled value of the active item(s).</p>\n<p>Use this when you need to control the state of the items. Can be binded with <code>v-model</code></p>\n',
'type': 'string | string[]',
'required': false
},
Expand All @@ -55,9 +55,9 @@
},
{
'name': 'type',
'description': '<p>Determines whether one or multiple items can be opened at the same time.</p>\n',
'description': '<p>Determines whether a &quot;single&quot; or &quot;multiple&quot; items can be pressed at a time.</p>\n<p>This prop will be ignored if any of <code>v-model</code> or <code>defaultValue</code> is an defined, as the type will be inferred from the value.</p>\n',
'type': '\'single\' | \'multiple\'',
'required': true
'required': false
}
]" />

Expand Down
6 changes: 3 additions & 3 deletions docs/content/meta/ToggleGroupRoot.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
{
'name': 'defaultValue',
'description': '<p>The value of the item to show as pressed when initially rendered. Use when you do not need to control the state of the items.</p>\n',
'description': '<p>The default active value of the item(s).</p>\n<p>Use when you do not need to control the state of the item(s).</p>\n',
'type': 'string | string[]',
'required': false
},
Expand All @@ -42,7 +42,7 @@
},
{
'name': 'modelValue',
'description': '<p>The controlled value of the pressed item. Can be bind as <code>v-model</code>.</p>\n',
'description': '<p>The controlled value of the active item(s).</p>\n<p>Use this when you need to control the state of the items. Can be binded with <code>v-model</code></p>\n',
'type': 'string | string[]',
'required': false
},
Expand All @@ -61,7 +61,7 @@
},
{
'name': 'type',
'description': '<p>Determines whether a single or multiple items can be pressed at a time.</p>\n',
'description': '<p>Determines whether a &quot;single&quot; or &quot;multiple&quot; items can be pressed at a time.</p>\n<p>This prop will be ignored if any of <code>v-model</code> or <code>defaultValue</code> is an defined, as the type will be inferred from the value.</p>\n',
'type': '\'single\' | \'multiple\'',
'required': false,
'default': '\'single\''
Expand Down
6 changes: 3 additions & 3 deletions docs/content/meta/ToolbarToggleGroup.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
{
'name': 'defaultValue',
'description': '<p>The value of the item to show as pressed when initially rendered. Use when you do not need to control the state of the items.</p>\n',
'description': '<p>The default active value of the item(s).</p>\n<p>Use when you do not need to control the state of the item(s).</p>\n',
'type': 'string | string[]',
'required': false
},
Expand All @@ -40,7 +40,7 @@
},
{
'name': 'modelValue',
'description': '<p>The controlled value of the pressed item. Can be bind as <code>v-model</code>.</p>\n',
'description': '<p>The controlled value of the active item(s).</p>\n<p>Use this when you need to control the state of the items. Can be binded with <code>v-model</code></p>\n',
'type': 'string | string[]',
'required': false
},
Expand All @@ -58,7 +58,7 @@
},
{
'name': 'type',
'description': '<p>Determines whether a single or multiple items can be pressed at a time.</p>\n',
'description': '<p>Determines whether a &quot;single&quot; or &quot;multiple&quot; items can be pressed at a time.</p>\n<p>This prop will be ignored if any of <code>v-model</code> or <code>defaultValue</code> is an defined, as the type will be inferred from the value.</p>\n',
'type': '\'single\' | \'multiple\'',
'required': false
}
Expand Down
48 changes: 17 additions & 31 deletions packages/radix-vue/src/Accordion/AccordionRoot.vue
Original file line number Diff line number Diff line change
@@ -1,26 +1,11 @@
<script lang="ts">
import type { ComputedRef, Ref } from 'vue'
import type { PrimitiveProps } from '@/Primitive'
import type { DataOrientation, Direction, Type } from '@/shared/types'
import type { DataOrientation, Direction, SingleOrMultipleProps, SingleOrMultipleType } from '@/shared/types'
import { createContext, useDirection, useForwardExpose } from '@/shared'

export interface AccordionRootProps extends PrimitiveProps {
/**
* Determines whether one or multiple items can be opened at the same time.
*/
type: Type

/**
* The controlled value of the item to expand when type is "single" or the controlled values of the items to expand when type is "multiple".
*/
modelValue?: string | string[]

/**
* The default value of the item to expand when type is "single" or the default values of the items to expand when type is "multiple".
* Use when you do not need to control the state of the item(s).
*/
defaultValue?: string | string[]

export interface AccordionRootProps<ValidValue = string | string[], ExplicitType = SingleOrMultipleType>
extends PrimitiveProps, SingleOrMultipleProps<ValidValue, ExplicitType> {
/**
* When type is "single", allows closing content when clicking trigger for an open item.
* When type is "multiple", this prop has no effect.
Expand Down Expand Up @@ -51,17 +36,17 @@ export interface AccordionRootProps extends PrimitiveProps {
orientation?: DataOrientation
}

export type AccordionRootEmits = {
export type AccordionRootEmits<T extends SingleOrMultipleType = SingleOrMultipleType> = {
/**
* Event handler called when the expanded state of an item changes
*/
'update:modelValue': [value: string | string[] | undefined]
'update:modelValue': [value: (T extends 'single' ? string : string[]) | undefined]
}

export type AccordionRootContext = {
disabled: Ref<AccordionRootProps['disabled']>
direction: Ref<AccordionRootProps['dir']>
orientation: AccordionRootProps['orientation']
export type AccordionRootContext<P extends AccordionRootProps> = {
disabled: Ref<P['disabled']>
direction: Ref<P['dir']>
orientation: P['orientation']
parentElement: Ref<HTMLElement | undefined>
changeModelValue(value: string): void
isSingle: ComputedRef<boolean>
Expand All @@ -70,20 +55,21 @@ export type AccordionRootContext = {
}

export const [injectAccordionRootContext, provideAccordionRootContext]
= createContext<AccordionRootContext>('AccordionRoot')
= createContext<AccordionRootContext<AccordionRootProps>>('AccordionRoot')
</script>

<script setup lang="ts">
<script setup lang="ts" generic="ValidValue extends (string | string[]), ExplicitType extends SingleOrMultipleType">
import { Primitive } from '@/Primitive'
import { useSingleOrMultipleValue } from '@/shared/useSingleOrMultipleValue'
import { computed, toRefs } from 'vue'
import { toRefs } from 'vue'

const props = withDefaults(defineProps<AccordionRootProps>(), {
const props = withDefaults(defineProps<AccordionRootProps<ValidValue, ExplicitType>>(), {
disabled: false,
orientation: 'vertical',
collapsible: false,
})
const emits = defineEmits<AccordionRootEmits>()

const emits = defineEmits<AccordionRootEmits<ExplicitType>>()

defineSlots<{
default(props: {
Expand All @@ -95,7 +81,7 @@ defineSlots<{
const { dir, disabled } = toRefs(props)
const direction = useDirection(dir)

const { modelValue, changeModelValue } = useSingleOrMultipleValue(props, emits)
const { modelValue, changeModelValue, isSingle } = useSingleOrMultipleValue(props, emits)

const { forwardRef, currentElement: parentElement } = useForwardExpose()

Expand All @@ -104,7 +90,7 @@ provideAccordionRootContext({
direction,
orientation: props.orientation,
parentElement,
isSingle: computed(() => props.type === 'single'),
isSingle,
collapsible: props.collapsible,
modelValue,
changeModelValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const items = ['One', 'Two', 'Three', 'Four']
<Variant title="Controlled (Single open)">
<AccordionRoot
class="w-[300px] rounded-md bg-[--line-color] shadow-lg"
type="multiple"
type="single"
model-value="Three"
>
<AccordionItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ const selectedValue = ref('item-2')
>
<Variant title="Uncontrolled">
<AccordionRoot
v-model="selectedValue"
class="w-[300px] rounded-md bg-[--line-color] shadow-lg"
type="single"
>
<AccordionItem class="accordion-item" value="item-1">
<AccordionHeader class="flex">
Expand Down Expand Up @@ -66,7 +66,6 @@ const selectedValue = ref('item-2')
<AccordionRoot
v-model="selectedValue"
class="w-[300px] rounded-md bg-[--line-color] shadow-lg"
type="single"
>
<AccordionItem class="accordion-item" value="item-1">
<AccordionHeader class="flex">
Expand Down
2 changes: 1 addition & 1 deletion packages/radix-vue/src/ToggleGroup/ToggleGroupItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const { forwardRef } = useForwardExpose()
:ref="forwardRef"
:disabled="disabled"
:pressed="
rootContext.type === 'single'
rootContext.isSingle
? rootContext.modelValue.value === value
: rootContext.modelValue.value?.includes(value)
"
Expand Down
21 changes: 7 additions & 14 deletions packages/radix-vue/src/ToggleGroup/ToggleGroupRoot.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
<script lang="ts">
import type { Ref } from 'vue'
import type { ComputedRef, Ref } from 'vue'
import type { PrimitiveProps } from '@/Primitive'
import type { DataOrientation, Direction } from '../shared/types'
import type { DataOrientation, Direction, SingleOrMultipleProps, SingleOrMultipleType } from '../shared/types'
import { createContext, useDirection, useForwardExpose } from '@/shared'

type TypeEnum = 'single' | 'multiple'

export interface ToggleGroupRootProps extends PrimitiveProps {
/** Determines whether a single or multiple items can be pressed at a time. */
type?: TypeEnum
/** The value of the item to show as pressed when initially rendered. Use when you do not need to control the state of the items. */
defaultValue?: string | string[]
/** The controlled value of the pressed item. Can be bind as `v-model`. */
modelValue?: string | string[]
export interface ToggleGroupRootProps<ValidValue = string | string[], ExplicitType = SingleOrMultipleType>
extends PrimitiveProps, SingleOrMultipleProps<ValidValue, ExplicitType> {
/** When `false`, navigating through the items using arrow keys will be disabled. */
rovingFocus?: boolean
/** When `true`, prevents the user from interacting with the toggle group and all its items. */
Expand All @@ -30,7 +23,7 @@ export type ToggleGroupRootEmits = {
}

interface ToggleGroupRootContext {
type: TypeEnum
isSingle: ComputedRef<boolean>
modelValue: Ref<string | string[] | undefined>
changeModelValue: (value: string) => void
dir?: Ref<Direction>
Expand Down Expand Up @@ -69,10 +62,10 @@ const { loop, rovingFocus, disabled, dir: propDir } = toRefs(props)
const dir = useDirection(propDir)
const { forwardRef } = useForwardExpose()

const { modelValue, changeModelValue } = useSingleOrMultipleValue(props, emits)
const { modelValue, changeModelValue, isSingle } = useSingleOrMultipleValue(props, emits)

provideToggleGroupRootContext({
type: props.type,
isSingle,
modelValue,
changeModelValue,
dir,
Expand Down
38 changes: 37 additions & 1 deletion packages/radix-vue/src/shared/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,46 @@
type DataOrientation = 'vertical' | 'horizontal'
type Direction = 'ltr' | 'rtl'
/**
* @deprecated Use `AccordionType` instead
*/
type Type = 'single' | 'multiple'
type SingleOrMultipleType = 'single' | 'multiple'

interface SingleOrMultipleProps<ValidValue = string | string[], ExplicitType = SingleOrMultipleType> {
/**
* Determines whether a "single" or "multiple" items can be pressed at a time.
*
* This prop will be ignored if any of `v-model` or `defaultValue` is an defined, as the type will be inferred from the value.
*/
type?: ValidValue extends string
? 'single'
: ValidValue extends string[]
? 'multiple'
: ExplicitType extends 'single'
? 'single'
: ExplicitType extends 'multiple'
? 'multiple'
: never

/**
* The controlled value of the active item(s).
*
* Use this when you need to control the state of the items. Can be binded with `v-model`
*/
modelValue?: ValidValue

/**
* The default active value of the item(s).
*
* Use when you do not need to control the state of the item(s).
*/
defaultValue?: ValidValue

}

type ScrollBodyOption = {
padding?: boolean | number
margin?: boolean | number
}

export type { DataOrientation, Direction, Type, ScrollBodyOption }
export type { DataOrientation, Direction, Type, SingleOrMultipleProps, SingleOrMultipleType, ScrollBodyOption }