Skip to content

Commit

Permalink
fix(ButtonGroup/FormGroup): pass default sizes to children
Browse files Browse the repository at this point in the history
  • Loading branch information
ManukMinasyan committed Jun 23, 2024
1 parent 431daf3 commit 7478903
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 51 deletions.
55 changes: 31 additions & 24 deletions src/components/forms/FormGroup.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
<template>
<div :class="ui.wrapper" v-bind="attrs">
<div v-if="label || $slots.label" :class="[ui.label.wrapper, size]">
<label :for="inputId" :class="[ui.label.base, required ? ui.label.required : '']">
<slot v-if="$slots.label" name="label" v-bind="{ error, label, name, hint, description, help }" />
<template v-else>{{ label }}</template>
</label>
<span v-if="hint || $slots.hint" :class="[ui.hint]">
<slot v-if="$slots.hint" name="hint" v-bind="{ error, label, name, hint, description, help }" />
<template v-else>{{ hint }}</template>
</span>
</div>
<div :class="ui.inner">
<div v-if="label || $slots.label" :class="[ui.label.wrapper, size]">
<label :for="inputId" :class="[ui.label.base, required ? ui.label.required : '']">
<slot v-if="$slots.label" name="label" v-bind="{ error, label, name, hint, description, help }" />
<template v-else>{{ label }}</template>
</label>
<span v-if="hint || $slots.hint" :class="[ui.hint]">
<slot v-if="$slots.hint" name="hint" v-bind="{ error, label, name, hint, description, help }" />
<template v-else>{{ hint }}</template>
</span>
</div>

<p v-if="description || $slots.description" :class="[ui.description, size]">
<slot v-if="$slots.description" name="description" v-bind="{ error, label, name, hint, description, help }" />
<template v-else>
{{ description }}
</template>
</p>
<p v-if="description || $slots.description" :class="[ui.description, size]">
<slot v-if="$slots.description" name="description" v-bind="{ error, label, name, hint, description, help }" />
<template v-else>
{{ description }}
</template>
</p>
</div>

<div :class="[label ? ui.container : '']">
<slot v-bind="{ error }" />
Expand All @@ -42,10 +44,10 @@ import { computed, defineComponent, provide, inject, ref, toRef } from 'vue'
import type { Ref, PropType } from 'vue'
import { useUI } from '../../composables/useUI'
import { mergeConfig } from '../../utils'
import type { FormError, InjectedFormGroupValue, Strategy } from '@/types'
import type { FormError, InjectedFormGroupValue, FormGroupSize, Strategy } from '@/types'
import appConfig from '@/constants/app.config'
import { formGroup } from '@/ui.config'
import { uid } from '@/utils/uid'
import { useId } from '@/composables/useId'
const config = mergeConfig<typeof formGroup>(appConfig.ui.strategy, appConfig.ui.formGroup, formGroup)
Expand All @@ -57,7 +59,7 @@ export default defineComponent({
default: null
},
size: {
type: String as PropType<keyof typeof config.size>,
type: String as PropType<FormGroupSize>,
default: null,
validator (value: string) {
return Object.keys(config.size).includes(value)
Expand Down Expand Up @@ -89,11 +91,15 @@ export default defineComponent({
},
class: {
type: [String, Object, Array] as PropType<any>,
default: undefined
default: () => ''
},
ui: {
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
default: undefined
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
default: () => ({})
},
eagerValidation: {
type: Boolean,
default: false
}
},
setup (props) {
Expand All @@ -108,13 +114,14 @@ export default defineComponent({
})
const size = computed(() => ui.value.size[props.size ?? config.default.size])
const inputId = ref(uid())
const inputId = ref(useId())
provide<InjectedFormGroupValue>('form-group', {
error,
inputId,
name: computed(() => props.name),
size: computed(() => props.size)
size: computed(() => props.size),
eagerValidation: computed(() => props.eagerValidation)
})
return {
Expand Down
6 changes: 3 additions & 3 deletions src/components/forms/Input.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div :class="ui.wrapper">
<div :class="(type === 'hidden') ? 'hidden' : ui.wrapper">
<input
:id="inputId"
ref="input"
Expand Down Expand Up @@ -170,7 +170,7 @@ export default defineComponent({
const { emitFormBlur, emitFormInput, size: sizeFormGroup, color, inputId, name } = useFormGroup(props, config)
const size = computed(() => sizeButtonGroup.value || sizeFormGroup.value)
const size = computed(() => sizeButtonGroup.value ?? sizeFormGroup.value)
const modelModifiers = ref(defu({}, props.modelModifiers, { trim: false, lazy: false, number: false }))
Expand Down Expand Up @@ -239,7 +239,7 @@ export default defineComponent({
ui.value.form,
rounded.value,
ui.value.placeholder,
props.type === 'file' && [ui.value.file.base, ui.value.file.padding[size.value]],
props.type === 'file' && ui.value.file.base,
ui.value.size[size.value],
props.padded ? ui.value.padding[size.value] : 'p-0',
variant?.replaceAll('{color}', color.value),
Expand Down
2 changes: 1 addition & 1 deletion src/components/forms/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export default defineComponent({
const { emitFormChange, inputId, color, size: sizeFormGroup, name } = useFormGroup(props, config)
const size = computed(() => sizeButtonGroup.value || sizeFormGroup.value)
const size = computed(() => sizeButtonGroup.value ?? sizeFormGroup.value)
const onInput = (event: Event) => {
emit('update:modelValue', (event.target as HTMLInputElement).value)
Expand Down
21 changes: 18 additions & 3 deletions src/composables/useButtonGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ type ButtonGroupProps = {
// make a ButtonGroupContext type for injection. Should include ButtonGroupProps
type ButtonGroupContext = {
children: ComponentInternalInstance[]
register(child: ComponentInternalInstance): void
unregister(child: ComponentInternalInstance): void
register (child: ComponentInternalInstance): void
unregister (child: ComponentInternalInstance): void
orientation: 'horizontal' | 'vertical'
size: string
ui: Partial<typeof buttonGroup>
Expand Down Expand Up @@ -42,6 +42,17 @@ export function useProvideButtonGroup (buttonGroupProps: ButtonGroupProps) {
export function useInjectButtonGroup ({ ui, props }: { ui: any, props: any }) {
const instance = getCurrentInstance()

provide('ButtonGroupContextConsumer', true)
const isParentPartOfGroup = inject('ButtonGroupContextConsumer', false)

// early return if a parent is already part of the group
if (isParentPartOfGroup) {
return {
size: computed(() => props.size),
rounded: computed(() => ui.value.rounded)
}
}

let parent = instance.parent
let groupContext: Ref<ButtonGroupContext> | undefined

Expand All @@ -61,8 +72,12 @@ export function useInjectButtonGroup ({ ui, props }: { ui: any, props: any }) {
onUnmounted(() => {
groupContext?.value.unregister(instance)
})

return {
size: computed(() => groupContext?.value.size || props.size),
size: computed(() => {
if (!groupContext?.value) return props.size
return groupContext?.value.size ?? ui.value.default.size
}),
rounded: computed(() => {
if (!groupContext || positionInGroup.value === -1) return ui.value.rounded
if (groupContext.value.children.length === 1) return groupContext.value.ui.rounded
Expand Down
42 changes: 25 additions & 17 deletions src/composables/useFormGroup.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
import { inject, ref, computed, onMounted } from 'vue'
import { inject, ref, computed } from 'vue'
import { type UseEventBusReturn, useDebounceFn } from '@vueuse/core'
import type { FormEvent, FormEventType, InjectedFormGroupValue } from '../types/form'
import { mergeConfig } from '@/utils'
import appConfig from '@/constants/app.config'
import { formGroup } from '@/ui.config'

type InputProps = {
id?: string
size?: string | number | symbol
color?: string
name?: string
eagerValidation?: boolean
legend?: string | null
}


const formGroupConfig = mergeConfig<typeof formGroup>(appConfig.ui.strategy, appConfig.ui.formGroup, formGroup)

export const useFormGroup = (inputProps?: InputProps, config?: any) => {
const formBus = inject<UseEventBusReturn<FormEvent, string> | undefined>('form-events', undefined)
const formGroup = inject<InjectedFormGroupValue | undefined>('form-group', undefined)
const formInputs = inject<any>('form-inputs', undefined)

const inputId = ref(inputProps?.id)

onMounted(() => {
inputId.value = inputProps?.id ?? formGroup?.inputId.value
if (formGroup) {
if (formGroup) {
if (inputProps?.id) {
// Updates for="..." attribute on label if inputProps.id is provided
formGroup.inputId.value = inputId.value
formGroup.inputId.value = inputProps?.id
}

if (formInputs) {
formInputs.value[formGroup.name.value] = inputId
}
if (formInputs) {
formInputs.value[formGroup.name.value] = formGroup.inputId.value
}
})
}

const blurred = ref(false)

Expand All @@ -37,24 +42,27 @@ export const useFormGroup = (inputProps?: InputProps, config?: any) => {
}

function emitFormBlur () {
emitFormEvent('blur', formGroup?.name.value)
emitFormEvent('blur', formGroup?.name.value as string)
blurred.value = true
}

function emitFormChange () {
emitFormEvent('change', formGroup?.name.value)
emitFormEvent('change', formGroup?.name.value as string)
}

const emitFormInput = useDebounceFn(() => {
if (blurred.value) {
emitFormEvent('input', formGroup?.name.value)
if (blurred.value || formGroup?.eagerValidation.value) {
emitFormEvent('input', formGroup?.name.value as string)
}
}, 300)

return {
inputId,
inputId: computed(() => inputProps?.id ?? formGroup?.inputId.value),
name: computed(() => inputProps?.name ?? formGroup?.name.value),
size: computed(() => inputProps?.size ?? formGroup?.size.value ?? config?.default?.size),
size: computed(() => {
const formGroupSize = config.size[formGroup?.size.value as string] ? formGroup?.size.value : null
return inputProps?.size ?? formGroupSize ?? formGroupConfig?.default?.size
}),
color: computed(() => formGroup?.error?.value ? 'red' : inputProps?.color),
emitFormBlur,
emitFormInput,
Expand Down
3 changes: 3 additions & 0 deletions src/types/form-group.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { formGroup } from '../ui.config'

export type FormGroupSize = keyof typeof formGroup.size
9 changes: 6 additions & 3 deletions src/types/form.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Ref } from 'vue'
import type { Ref } from 'vue'

export interface FormError<T extends string = string> {
path: T
Expand All @@ -10,11 +10,13 @@ export interface FormErrorWithId extends FormError {
}

export interface Form<T> {
validate(path?: string, opts?: { silent?: boolean }): Promise<T>
validate(path?: string | string[], opts?: { silent?: true }): Promise<T | false>;
validate(path?: string | string[], opts?: { silent?: false }): Promise<T>;
clear(path?: string): void
errors: Ref<FormError[]>
setErrors(errs: FormError[], path?: string): void
getErrors(path?: string): FormError[]
submit(): Promise<void>
}

export type FormSubmitEvent<T> = SubmitEvent & { data: T }
Expand All @@ -31,5 +33,6 @@ export interface InjectedFormGroupValue {
inputId: Ref<string | undefined>
name: Ref<string>
size: Ref<string | number | symbol>
error: Ref<string | boolean>
error: Ref<string | boolean | undefined>
eagerValidation: Ref<boolean>
}
1 change: 1 addition & 0 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from './clipboard'
export * from './command-palette'
export * from './dropdown'
export * from './form'
export * from './form-group'
export * from './input'
export * from './link'
export * from './meter'
Expand Down

0 comments on commit 7478903

Please sign in to comment.