feat(RadioGroup): new component (#730)

Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
Romain Hamel
2023-10-27 15:47:24 +02:00
committed by GitHub
parent f785ecd46f
commit 23d5dc7b98
11 changed files with 369 additions and 122 deletions

View File

@@ -2,7 +2,7 @@
<div :class="ui.wrapper">
<div class="flex items-center h-5">
<input
:id="inputId"
:id="id"
v-model="pick"
:name="name"
:required="required"
@@ -15,7 +15,7 @@
>
</div>
<div v-if="label || $slots.label" class="ms-3 text-sm">
<label :for="inputId" :class="ui.label">
<label :for="id" :class="ui.label">
<slot name="label">{{ label }}</slot>
<span v-if="required" :class="ui.required">*</span>
</label>
@@ -27,11 +27,10 @@
</template>
<script lang="ts">
import { computed, toRef, defineComponent } from 'vue'
import { computed, defineComponent, inject, toRef } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { useFormGroup } from '../../composables/useFormGroup'
import { mergeConfig } from '../../utils'
import type { Strategy } from '../../types'
// @ts-expect-error
@@ -39,6 +38,7 @@ import appConfig from '#build/app.config'
import { radio } from '#ui/ui.config'
import colors from '#ui-colors'
import { uid } from '../../utils/uid'
import { useFormGroup } from '../../composables/useFormGroup'
const config = mergeConfig<typeof radio>(appConfig.ui.strategy, appConfig.ui.radio, radio)
@@ -98,11 +98,12 @@ export default defineComponent({
default: undefined
}
},
emits: ['update:modelValue'],
emits: ['update:modelValue', 'change'],
setup (props, { emit }) {
const { ui, attrs } = useUI('radio', toRef(props, 'ui'), config, toRef(props, 'class'))
const { emitFormChange, color, name, inputId } = useFormGroup(props)
const radioGroup = inject('radio-group', null)
const { emitFormChange, color, name } = radioGroup ?? useFormGroup(props, config)
const pick = computed({
get () {
@@ -110,7 +111,9 @@ export default defineComponent({
},
set (value) {
emit('update:modelValue', value)
if (value) {
emit('change', value)
if (!radioGroup) {
emitFormChange()
}
}
@@ -129,7 +132,6 @@ export default defineComponent({
return {
// eslint-disable-next-line vue/no-dupe-keys
ui,
inputId,
attrs,
pick,
// eslint-disable-next-line vue/no-dupe-keys

View File

@@ -0,0 +1,141 @@
<template>
<div :class="ui.wrapper">
<fieldset v-bind="attrs">
<legend v-if="legend || $slots.legend" :class="ui.legend">
<slot name="legend">
{{ legend }}
</slot>
</legend>
<URadio
v-for="option in normalizedOptions"
:key="option.value"
:label="option.label"
:model-value="modelValue"
:value="option.value"
:disabled="disabled"
@change="onUpdate(option.value)"
>
<template #label>
<slot name="label" v-bind="{ option }" />
</template>
</URadio>
</fieldset>
</div>
</template>
<script lang="ts">
import URadio from './Radio.vue'
import { computed, defineComponent, provide, toRef } from 'vue'
import type { PropType } from 'vue'
import { useUI } from '../../composables/useUI'
import { useFormGroup } from '../../composables/useFormGroup'
import { mergeConfig, get } from '../../utils'
import type { Strategy } from '../../types'
// @ts-expect-error
import appConfig from '#build/app.config'
import { radioGroup } from '#ui/ui.config'
import colors from '#ui-colors'
const config = mergeConfig<typeof radioGroup>(appConfig.ui.strategy, appConfig.ui.select, radioGroup)
export default defineComponent({
components: {
URadio
},
inheritAttrs: false,
props: {
modelValue: {
type: [String, Number, Object],
default: ''
},
name: {
type: String,
default: null
},
legend: {
type: String,
default: null
},
options: {
type: Array,
default: () => []
},
optionAttribute: {
type: String,
default: 'label'
},
valueAttribute: {
type: String,
default: 'value'
},
disabled: {
type: Boolean,
default: false
},
color: {
type: String as PropType<typeof colors[number]>,
default: () => config.default.color,
validator (value: string) {
return appConfig.ui.colors.includes(value)
}
},
class: {
type: [String, Object, Array] as PropType<any>,
default: undefined
},
ui: {
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
default: undefined
}
},
emits: ['update:modelValue', 'change'],
setup (props, { emit }) {
const { ui, attrs } = useUI('radioGroup', toRef(props, 'ui'), config, toRef(props, 'class'))
const { emitFormChange, color, name } = useFormGroup(props, config)
provide('radio-group', { color, name })
const onUpdate = (value: any) => {
emit('update:modelValue', value)
emit('change', value)
emitFormChange()
}
const guessOptionValue = (option: any) => {
return get(option, props.valueAttribute, get(option, props.optionAttribute))
}
const guessOptionText = (option: any) => {
return get(option, props.optionAttribute, get(option, props.valueAttribute))
}
const normalizeOption = (option: any) => {
if (['string', 'number', 'boolean'].includes(typeof option)) {
return {
value: option,
label: option
}
}
return {
...option,
value: guessOptionValue(option),
label: guessOptionText(option)
}
}
const normalizedOptions = computed(() => {
return props.options.map(option => normalizeOption(option))
})
return {
// eslint-disable-next-line vue/no-dupe-keys
ui,
attrs,
normalizedOptions,
// eslint-disable-next-line vue/no-dupe-keys
onUpdate
}
}
})
</script>