Skip to content

Commit

Permalink
refactor: radio-group
Browse files Browse the repository at this point in the history
refactor: radio-group
  • Loading branch information
Cr0zy07 committed Dec 15, 2023
1 parent 194123a commit 6f08f41
Show file tree
Hide file tree
Showing 29 changed files with 1,732 additions and 1,189 deletions.
4 changes: 2 additions & 2 deletions packages/components/radio-group/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ A set of checkable buttons—known as radio buttons—where no more than one of

![@oku-ui/radio-group](./../../../.github/assets/og/oku-radio-group.jpg)

<span><a href="https://www.npmjs.com/package/@oku-ui/radio-group "><img src="https://img.shields.io/npm/v/@oku-ui/radio-group?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/radio-group"> <img src="https://img.shields.io/npm/dm/@oku-ui/radio-group?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/radio-group"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span>
[![Version](https://img.shields.io/npm/v/@oku-ui/radio-group?style=flat&colorA=18181B&colorB=28CF8D)](https://www.npmjs.com/package/@oku-ui/radio-group) [![Downloads](https://img.shields.io/npm/dm/@oku-ui/radio-group?style=flat&colorA=18181B&colorB=28CF8D)](https://www.npmjs.com/package/@oku-ui/radio-group)

## Installation

```sh
$ pnpm add @oku-ui/radio-group
```

[Documentation](https://oku-ui.com/primitives/components/radio-group)
[Documentation](https://oku-ui.com/primitives/components/radio-group)
83 changes: 0 additions & 83 deletions packages/components/radio-group/src/BubbleInput.ts

This file was deleted.

128 changes: 35 additions & 93 deletions packages/components/radio-group/src/Radio.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,17 @@
import { Primitive, primitiveProps } from '@oku-ui/primitive'
import type { OkuElement } from '@oku-ui/primitive'
import { createProvideScope } from '@oku-ui/provide'
import { reactiveOmit, useComposedRefs, useForwardRef } from '@oku-ui/use-composable'
import { computed, defineComponent, h, mergeProps, reactive, ref, toRefs } from 'vue'
import type { PropType, Ref } from 'vue'
import { reactiveOmit, useComposedRefs, useForwardRef, useListeners } from '@oku-ui/use-composable'
import { composeEventHandlers } from '@oku-ui/utils'
import { getState, scopeRadioProps } from './utils'
import { OkuBubbleInput } from './BubbleInput'

const RADIO_NAME = 'OkuRadio'

export const [createRadioProvide, createRadioScope] = createProvideScope(RADIO_NAME)

type RadioProvideValue = {
checked: Ref<boolean>
disabled?: Ref<boolean | undefined>
}

export const useRadioScope = createRadioScope()

export const [radioProvider, useRadioInject] = createRadioProvide<RadioProvideValue>(RADIO_NAME)

export type RadioIntrinsicNaviteElement = OkuElement<'button'>
export type RadioElement = HTMLButtonElement

export interface RadioProps {
checked?: boolean
required?: boolean
disabled?: boolean
value?: string
name?: string
}

export type RadioEmits = {
check: []
click: (event: MouseEvent) => void
}

export const radioProps = {
props: {
checked: {
type: Boolean as PropType<boolean | undefined>,
default: undefined,
},
required: {
type: Boolean as PropType<boolean | undefined>,
default: undefined,
},
disabled: {
type: Boolean as PropType<boolean | undefined>,
default: undefined,
},
name: {
type: String as PropType<string | undefined>,
default: undefined,
},
value: {
type: String as PropType<string>,
default: 'on',
},
...primitiveProps,
},
emits: {
check: () => true,
// eslint-disable-next-line unused-imports/no-unused-vars
click: (event: MouseEvent) => true,
},
}
import { Primitive } from '@oku-ui/primitive'
import { OkuBubbleInput } from './bubble-input'
import { getState } from './utils'
import { RADIO_NAME, radioProps, radioProvider, scopeRadioProps } from './props'
import type { RadioEmits, RadioNativeElement } from './props'

const radio = defineComponent({
name: RADIO_NAME,
components: {
OkuBubbleInput,
},
inheritAttrs: false,
props: {
...radioProps.props,
Expand All @@ -80,79 +22,79 @@ const radio = defineComponent({
const {
scopeOkuRadio,
name,
// checked,
checked: checkedProp,
required,
disabled,
value,
...groupProps
...radioProps
} = toRefs(props)
const checked = computed(() => checkedProp.value || false)
const _reactive = reactive(groupProps)
const reactiveGroupProps = reactiveOmit(_reactive, (key, _value) => key === undefined)

const hasConsumerStoppedPropagationRef = ref(false)
const buttonRef = ref<HTMLButtonElement | null>(null)
const _reactive = reactive(radioProps)
const otherProps = reactiveOmit(_reactive, (key, _value) => key === undefined)

const forwardedRef = useForwardRef()
const composedRefs = useComposedRefs(buttonRef, forwardedRef)
const emits = useListeners()

const checked = computed(() => checkedProp.value || false)

const buttonRef = ref<HTMLButtonElement | null>(null)
const composedRefs = useComposedRefs(forwardedRef, buttonRef)
const hasConsumerStoppedPropagationRef = ref(false)
// We set this to true by default so that events bubble to forms without JS (SSR)
const isFormControl = computed(() => buttonRef.value ? Boolean(buttonRef.value.closest('form')) : false)

radioProvider({
scope: scopeOkuRadio.value,
checked,
disabled,
scope: scopeOkuRadio.value,
})

return () => [
h(Primitive.button, {
'type': 'button',
'role': 'radio',
'aria-checked': checked.value,
'data-state': getState(checked.value || false),
'data-state': computed(() => getState(checked.value)).value,
'data-disabled': disabled.value ? '' : undefined,
'disabled': disabled.value,
'value': value.value,
...mergeProps(attrs, reactiveGroupProps),
...mergeProps(attrs, otherProps, emits),
'ref': composedRefs,
'onClick': composeEventHandlers((e: MouseEvent) => {
emit('click', e)
}, (event: MouseEvent) => {
'onClick': composeEventHandlers<RadioEmits['click'][0]>((event) => {
emit('click', event)
}, (event) => {
// radios cannot be unchecked so we only communicate a checked state
if (!checked.value)
emit('check')
if (isFormControl.value) {
// TODO: check `isPropagationStopped`
// TODO: isPropagationStopped() is not supported in vue
// hasConsumerStoppedPropagationRef.value = event.isPropagationStopped()

// if radio is in a form, stop propagation from the button so that we only propagate
// one click event (from the input). We propagate changes from an input so that native
// form validation works and form events reflect radio updates.
if (!hasConsumerStoppedPropagationRef.value)
event.stopPropagation()
}
}),
}, {
default: () => slots.default?.(),
}),
}, () => slots.default?.()),

isFormControl.value && h(OkuBubbleInput, {
control: buttonRef.value,
bubbles: !hasConsumerStoppedPropagationRef.value,
bubbles: computed(() => !hasConsumerStoppedPropagationRef.value).value,
name: name.value,
value: value.value,
checked: checked.value || false,
checked: checked.value,
required: required.value,
disabled: disabled.value,
// We transform because the input is absolutely positioned but we have
// rendered it **after** the button. This pulls it back to sit on top
// of the button.
style: {
transform: 'translateX(-100%)',
},
style: { transform: 'translateX(-100%)' },
}),
]
},
})

export const OkuRadio = radio as typeof radio &
(new () => {
$props: RadioIntrinsicNaviteElement
})
export const OkuRadio = radio as typeof radio & (new () => { $props: RadioNativeElement })

0 comments on commit 6f08f41

Please sign in to comment.