-
-
Notifications
You must be signed in to change notification settings - Fork 35
/
RadioGroupItem.ts
130 lines (110 loc) · 4.06 KB
/
RadioGroupItem.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { computed, defineComponent, h, onMounted, onUnmounted, ref, toRefs } from 'vue'
import { useComposedRefs, useForwardRef } from '@oku-ui/use-composable'
import { OkuRovingFocusGroupItem } from '@oku-ui/roving-focus'
import { composeEventHandlers } from '@oku-ui/utils'
import { useRadioGroupInject, useRovingFocusGroupScope } from './RadioGroup'
import type { RadioGroupIntrinsicElement } from './RadioGroup'
import type { RadioElement, RadioProps } from './Radio'
import { OkuRadio, radioPropsObject, useRadioScope } from './Radio'
import type { ScopeRadioGroup } from './utils'
import { ARROW_KEYS, scopeRadioGroupProps } from './utils'
const ITEM_NAME = 'OkuRadioGroupItem'
export type RadioGroupItemIntrinsicElement = RadioGroupIntrinsicElement
export type RadioGroupItemElement = HTMLDivElement
interface RadioGroupItemProps extends Omit<RadioProps, 'onCheck' | 'name'> {
value: string
}
// eslint-disable-next-line unused-imports/no-unused-vars
const { name, ...radioProps } = radioPropsObject
const radioGroupItemPropsObject = {
}
const RadioGroupItem = defineComponent({
name: ITEM_NAME,
inheritAttrs: false,
props: {
...radioGroupItemPropsObject,
...radioProps,
...scopeRadioGroupProps,
},
emits: {
'update:modelValue': (value: string) => true,
'focus': (event: FocusEvent) => true,
},
setup(props, { slots, emit, attrs }) {
const {
disabled,
required,
value,
} = toRefs(props)
const inject = useRadioGroupInject(ITEM_NAME, props.scopeOkuRadioGroup)
const isDisabled = computed(() => disabled.value || inject.disabled.value)
const rovingFocusGroupScope = useRovingFocusGroupScope(props.scopeOkuRadioGroup)
const radioScope = useRadioScope(props.scopeOkuRadioGroup)
const rootRef = ref<RadioElement | null>(null)
const forwardedRef = useForwardRef()
const composedRefs = useComposedRefs(rootRef, forwardedRef)
const checked = computed(() => inject.value?.value === value.value)
const isArrowKeyPressedRef = ref(false)
const handleKeyDown = (event: KeyboardEvent) => {
if (ARROW_KEYS.includes(event.key))
isArrowKeyPressedRef.value = true
}
const handleKeyUp = () => {
isArrowKeyPressedRef.value = false
}
onMounted(() => {
document.addEventListener('keydown', handleKeyDown)
document.addEventListener('keyup', handleKeyUp)
})
onUnmounted(() => {
document.removeEventListener('keydown', handleKeyDown)
document.removeEventListener('keyup', handleKeyUp)
})
return () => h(OkuRovingFocusGroupItem, {
asChild: true,
...rovingFocusGroupScope,
focusable: !isDisabled.value,
active: checked.value,
}, {
default: () => h(OkuRadio, {
disabled: isDisabled.value,
required: inject.required.value || required.value,
checked: checked.value,
...radioScope,
...attrs,
value: value.value,
name: inject.name?.value,
ref: composedRefs,
onCheck: () => {
return inject.onValueChange(props.value)
},
onKeydown: composeEventHandlers((event: any) => {
// According to WAI ARIA, radio groups don't activate items on enter keypress
if (event.key === 'Enter')
event.preventDefault()
}),
onFocus: composeEventHandlers<FocusEvent>((e) => {
emit('focus', e)
},
() => {
/**
* Our `RovingFocusGroup` will focus the radio when navigating with arrow keys
* and we need to "check" it in that case. We click it to "check" it (instead
* of updating `context.value`) so that the radio change event fires.
*/
setTimeout(() => {
if (isArrowKeyPressedRef.value)
rootRef.value?.click()
}, 0)
}),
}, {
default: () => slots.default?.(),
}),
})
},
})
export const OkuRadioGroupItem = RadioGroupItem as typeof RadioGroupItem &
(new () => {
$props: ScopeRadioGroup<Partial<RadioGroupItemIntrinsicElement>>
})
export type { RadioGroupItemProps }