Files
headlessui/packages/@headlessui-vue/src/components/description/description.ts
T
Robin Malfait a02c818f94 Use internal label and descriptions (#313)
* improve internal Label component

We will now add a name to improve error messages, we also introduced a
`clickable` prop on the label.

Not 100% happy with the implementation of these internal Label &
Description components, but they are internal so we can always change it
to something that makes more sense!

* improve internal Description component

We will now add a name to improve error messages.

* provide the name prop to Description & Label providers

* implement the useLabels and useDescriptions in the Switch components

* update documentation
2021-04-08 17:39:02 +02:00

102 lines
2.3 KiB
TypeScript

import {
computed,
defineComponent,
inject,
onMounted,
onUnmounted,
provide,
ref,
// Types
ComputedRef,
InjectionKey,
Ref,
} from 'vue'
import { useId } from '../../hooks/use-id'
import { render } from '../../utils/render'
// ---
let DescriptionContext = Symbol('DescriptionContext') as InjectionKey<{
register(value: string): () => void
slot: Ref<Record<string, any>>
name: Ref<string>
props: Ref<Record<string, any>>
}>
function useDescriptionContext() {
let context = inject(DescriptionContext, null)
if (context === null) {
throw new Error('Missing parent')
}
return context
}
export function useDescriptions(): [
ComputedRef<string | undefined>,
ReturnType<typeof defineComponent>
] {
let descriptionIds = ref<string[]>([])
return [
// The actual id's as string or undefined.
computed(() => (descriptionIds.value.length > 0 ? descriptionIds.value.join(' ') : undefined)),
// The provider component
defineComponent({
name: 'DescriptionProvider',
props: ['slot', 'name', 'props'],
setup(props, { slots }) {
function register(value: string) {
descriptionIds.value.push(value)
return () => {
let idx = descriptionIds.value.indexOf(value)
if (idx === -1) return
descriptionIds.value.splice(idx, 1)
}
}
provide(DescriptionContext, {
register,
slot: computed(() => props.slot),
name: computed(() => props.name),
props: computed(() => props.props),
})
return () => slots.default!()
},
}),
]
}
// ---
export let Description = defineComponent({
name: 'Description',
props: {
as: { type: [Object, String], default: 'p' },
},
render() {
let passThroughProps = this.$props
let propsWeControl = { ...this.props, id: this.id }
return render({
props: { ...this.props, ...passThroughProps, ...propsWeControl },
slot: this.slot || {},
attrs: this.$attrs,
slots: this.$slots,
name: this.name || 'Description',
})
},
setup() {
let { register, slot, name, props } = useDescriptionContext()
let id = `headlessui-description-${useId()}`
onMounted(() => onUnmounted(register(id)))
return { id, slot, name, props }
},
})