See discussion here.
This package serves as a POC that:
- Allows any level of component encapsulation for child components.
- Supports dynamically toggling children with
v-if
and re-ordering withv-for
. - Support TypeScript.
- Whose implementation details are abstracted away from the consumer components.
npm i --save vue-coupled
eg. A <Select>
/ <Option>
couple:
import type { VNode } from 'vue'
import { createCoupled } from 'vue-coupled'
type Option = {
// props
value: string | number
label: string
disabled?: boolean
// computed states or slot renderers
render?: () => VNode
}
export const { useParent, useChild } = createCoupled<Option>()
<script lang="ts">
import { h, defineComponent, renderSlot } from 'vue'
import { useParent } from './option-group'
export default defineComponent({
setup(_, { slots }) {
const { children: options } = useParent()
return () =>
h('ul', { class: 'select' }, [
renderSlot(slots, 'default'),
...options.value.map((option) =>
h(
'li',
{ class: 'option' },
option.render ? option.render() : option.label
)
),
])
},
})
</script>
<script setup lang="ts">
import { renderSlot, useSlots } from 'vue'
import { reactiveComputed } from '@vueuse/core'
import { useChild } from './option-group'
// Vue doesn't support imported type in defineProps transformation yet
type OptionProps = {
value: string | number
label: string
disabled?: boolean
}
const slots = useSlots()
const props = defineProps<OptionProps>()
// Use reactiveComputed from `@vueuse/core` so that we can use computed
// objects instead of refs.
const child = reactiveComputed(() => {
return {
...props,
render: () => renderSlot(slots, 'default', undefined, () => [props.label]),
}
})
useChild(child)
</script>
<template>
<!-- Render a placeholder here -->
<div v-if="false" />
</template>