Skip to content

Commit 4ef2ab9

Browse files
committed
Add examples.md
1 parent c31719a commit 4ef2ab9

19 files changed

+590
-1
lines changed

docs/.vitepress/config.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default defineConfigWithTheme({
2626

2727
sidebar: [
2828
{
29-
text: 'Guide',
29+
text: 'Getting started',
3030
items: [
3131
{
3232
text: 'What is vue-vnode-utils?',
@@ -36,6 +36,14 @@ export default defineConfigWithTheme({
3636
link: '/guide/installation.html'
3737
}
3838
]
39+
}, {
40+
text: 'Reference',
41+
items: [
42+
{
43+
text: 'Examples',
44+
link: '/examples.html'
45+
}
46+
]
3947
}
4048
]
4149
}

docs/.vitepress/theme/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { App } from 'vue'
2+
import DefaultTheme from 'vitepress/theme'
3+
import LiveExample from '../../components/live-example.vue'
4+
5+
export default {
6+
...DefaultTheme,
7+
enhanceApp({ app }: { app: App }) {
8+
app.component('live-example', LiveExample)
9+
}
10+
}

docs/components/live-example.vue

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<template>
2+
<fieldset>
3+
<legend>Live Example</legend>
4+
<slot />
5+
</fieldset>
6+
</template>
7+
8+
<script>
9+
export default {
10+
name: 'live-example'
11+
}
12+
</script>
13+
14+
<style scoped>
15+
fieldset {
16+
border: 1px solid #777;
17+
border-radius: 6px;
18+
padding: 10px;
19+
}
20+
21+
fieldset > ::v-deep(pre) {
22+
background: #eee;
23+
border: 1px solid #ccc;
24+
padding: 2px;
25+
}
26+
27+
fieldset > ::v-deep(pre::after) {
28+
content: " ";
29+
visibility: hidden;
30+
}
31+
32+
fieldset > legend {
33+
/*border: 1px solid #777;*/
34+
/*border-radius: 6px;*/
35+
padding: 0 5px;
36+
}
37+
</style>

docs/examples.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
<script setup>
2+
import AddClassExample from './examples/add-class-example.vue'
3+
import BasicAccordionExample from './examples/basic-accordion-example.vue'
4+
import WrapChildrenExample from './examples/wrap-children-example.vue'
5+
import SeparatorsExample from './examples/separators-example.vue'
6+
import EmptyListExample from './examples/empty-list-example.vue'
7+
import AddSlotRefExample from './examples/add-slot-ref-example.vue'
8+
import AddSlotRefsExample from './examples/add-slot-refs-example.vue'
9+
</script>
10+
# Examples
11+
12+
`vue-vnode-utils` are intended to manipulate VNodes in `render` functions, so the examples below are all using `render` functions instead of templates. `<script setup>` doesn't support `render` functions, but they can be written in various other ways:
13+
14+
```js
15+
export default {
16+
render() {
17+
// Options API render function
18+
}
19+
}
20+
```
21+
22+
```js
23+
export default {
24+
setup() {
25+
return () => {
26+
// Composition API render function
27+
}
28+
}
29+
}
30+
```
31+
32+
```js
33+
export default () => {
34+
// Functional components are just a render function
35+
}
36+
```
37+
38+
The examples use functional components where possible.
39+
40+
Templates are used for components that don't use `vue-vnode-utils` directly, as those don't need to use `render` functions.
41+
42+
## Adding a class
43+
44+
Props can be added to child VNodes using `addProps`. For a `class` prop this will be additive, it won't remove any other classes that are already included.
45+
46+
In the example below, the `<add-outline>` component adds the class `child-outline` to each of its children. This class applies the border and spacing seen in the live demo.
47+
48+
<<< @/examples/add-outline.js
49+
50+
Usage:
51+
52+
<<< @/examples/add-class-example.vue
53+
54+
<style>
55+
.child-outline {
56+
border: 1px solid #777;
57+
padding: 5px;
58+
}
59+
60+
.child-outline + .child-outline {
61+
margin-top: 10px;
62+
}
63+
</style>
64+
65+
<live-example>
66+
<add-class-example />
67+
</live-example>
68+
69+
## Adding component v-model
70+
71+
`addProps` can also be used to add the prop/event pair used for `v-model`. Here we'll use it to implement an accordion component.
72+
73+
:::tip
74+
This functionality would be better implemented using `provide` and `inject`. [Example here](https://skirtles-code.github.io/vue-examples/components/accordion.html).
75+
:::
76+
77+
The intended usage is something like this:
78+
79+
<<< @/examples/basic-accordion-example.vue
80+
81+
First we'll need a `<basic-accordion-panel>` component. It implements an `expanded` prop and `update:expanded` event, consistent with `v-model:expanded`:
82+
83+
<<< @/examples/basic-accordion-panel.vue
84+
85+
The `<basic-accordion>` might then be implemented something like this, using `addProps` to add the prop and event:
86+
87+
<<< @/examples/basic-accordion.vue
88+
89+
This implementation is very naive and is only intended to demonstrate the basic idea of how this component might be implemented using VNode manipulation.
90+
91+
All of which gives:
92+
93+
<live-example>
94+
<basic-accordion-example />
95+
</live-example>
96+
97+
See it on the SFC Playground: [Composition API](https://sfc.vuejs.org/#eNqtVk2P2zYQ/StT5WAv1pLcrxRQ7d0kQHsOkOYUB4VMUhazlKiSlNeGof/eISnJkmxvEiB7WXE4772Z4QzpU/C2qqJ9zYIkWGmieGVAM1NXD5uSF5VUBt6lmpO3hEhFuSwhU7KAWRSPzZZidgPyPi2ZuIFzey14FfsAUBoXhhWVSA3DFcBqa1Fh2sGc8dIcVk7KcCPYehP8zZU2m6B1BnBr8D5EloaVpuWJrxJ9m8oHhlx0IOMNP1znn5yroYxbf6/KxR5aV/Gg1sEi8IcYFmkVfdGyxM44Weim3dCbIAFnsTY8OrveBLkxlU7iWGfEnucXHUm1i/ErUnVpeMEipotwq+SzZgqJN8Gi43ijn7jCJK13uC8lZWFtuHBCZ+K6rJ52EZFFfMs/plybqXEkS9l+LI3OMW7smQoVKylTTL2UzsT1IiVL22zKBqt4OR/nETsP1wnyBSiWQdMOyGiOTpBS+l7JSvf7t3JH0KZkBwejLEtrgXAbjpvm+b8LJNNCGmS6604PuwbnAUEppkP9mK5tNPOyFuLOMlo3hQyqhPkdrB86KIBgBgnwaBGy7Fw7TpJzQbFKuNdlMHfqeAIutscI6R4f4dPnxZS44+AY1AEJnMj9/Vmij2gAAYhj+Kh5uQOTsxbLNUg8LnEEjQUV2BycTCDbGu+7Oss4YRoyqRCNKHZI0Z8NfbsqJV1c6/W4ctE+FTVrG8v/zWT5saI4V0nnOUtg3n1Ps7Z/PBvuTzbPQQwEsUAuoLFrA0xoLAPSvRjuN2vYhphIDJeDRf/Z9A3UH1g+n1G+n9leJCLVGhKY9TfRDJpF3ziI7cnsQE1fB22OgoEmsmIULVHP4hPa4oKpBH6uDqCl4BReEUL+tFt4R+Bdd0wgE+zgLPYjpFwxYpAgwYYTdVG6rZzxXW4S+HW5rLzzM6cmT+CX37zBB2aD8Xfnjdftyuvqm7xyw722I8tL5gfFZXDut/aEzLFiCbyTUrDU1bXBXrP/3fvQuyn2X42pIM6ovh099oNROB9dRd3ptDdAwe0U+xj+woWef5rVk879PAAYudth+dfD0bUk8wvUAn5yKUZ9V185zPFTjx3iuwPfvMkTtgkg6bZOfYkwne5VHIFzlmITIOYNEZw8ocXHjd6nky8bNM0KX439EL4PeYa+HTviO8KtpEcEr+xNBvHDGdl9jV7Sl7q0fdpd4b6jIbFyV1ju4TpxkaodL0MjKzcIPd7XpR2UlDztFN6wNEQViSPzir3OXmeZC+SFMSK10ta9khx/eihn84IJhF4N8McJpdhyCfw+kLdVvCme/ZGRr4vbAiWQ1kaOdJdO+SxvL/9MyOfe1QfQ940LYsqFFeumfnkx4M3/uGvBFg==) | [Options API](https://sfc.vuejs.org/#eNqtVttu2zgQ/ZVZtYBTxJKyty6gdZK2D/tcoNunqg8KNbLY0KSWpBwbhv99h6Quli9pCtQIYnI4c+bCM0PvovdNk6xbjLJoYZjmjb3LJV81Slv4UBjO3jOmdMmVhEqrFcySdCp2xrMLJh8LieKCnT/rjHOJG29eYlW0wsIulwBMEaZEaU0WBHAEPz8n9LjuYJ9L+lukQ1a0sbhqRGGRdgCLB2cYF72lF56K48ZnYbkVeJtH/3BtbB51ygB+D0GHKWkp3g4nPQv0Mi+fkLDKAzdB8NP9/FtzfejG73/Uy8kZSRfpQa2jeRT4Ea+KJvlmlCS6+SvNuwOTR8Ml5xGxwu3zqLa2MVmamoo5qnwzidLLlFaJbqXlK0zQrOIHrZ4MagLOo44TefTOPHJNSTrteC1ViXFrufCORuBWNo/LhJiWXtJPS27ssXDitsT11DUpp3SwRh1rlCVq1M+lc6R6klJPZqriaeud69sd1HPQWMG+671Ji+6gKMuPWjVmOL+U+8XWLAtbXL3pL0yjbbXsdwBkUVAupW/FDGQrfEP6LOgf1cl9h3xHFIGWKEfXCrdwE1TcDJDUXazmoiR9Ouljv7I1N8lrI5Q1dAM+tvuE0O7v4cvXOdDq9m4MKeBw8rghEO/m+rp3cpoBQJrCZ8PlEmyNnR03oOiixBYMlVIQLTibGDy0FkxbVZyhgUppsiUb3BSkjaNmX56sj+f21msmk7p1bHKfmZKfGyo5Zr3GLIOrfj1N1H14dXg6OYIznqggPpBDvT2gMJQ4IT0X5MvQRwJ04ONmWHaL/Zv+Urorqa9mJV/P5kRbJgpjIIPZMGdmRKaBHGR5fugbuxUIhqkGS5Ikg3kI/oE2qDP4tdmAUYKX8Iox9renOTc0wrYZVAI3XuIWcck1MksAGTFJtCvpj2rky9pm8PvNTROUn3hp6wx++yMIQmAumDASL7yHk44+13y44u5J/DJrj0jxteusxjXIME5Hug13ZbcNZvBBKYFFV+6+K+nQPQ0Hyhr/aylhsre6xYGWAeOT1dQkZ7p7hbZW5RiFVculwLHbO6q8dslcnWQyh18mTPJ323v47rtOhAlkoQfu6L3KI8j6o91QGsLsn8CJcY0FUYNs3jHB2SNJQhqkvduFQsF+v6AnYn1ovo55Rbo9Otn3gA+q3JLxwo0tSO9Gy341eTaf4273jodp/HKaUuXOoFzDeeBVoZdcxlY1vj0G+1CXrn0K9rjUNFDLmLwoaqRX+LZ6W1U+kGeai7XaOPVGcfqdob0sOMwgDt6IyjTviWAZ/Hng3lXxovPqr4p937krUAZFa9XE7433PLp3874S6mlQDQEMvPFBHGNRxfpZcHPS9vv/Ae6z2QY=)
98+
99+
## Wrap children
100+
101+
We can use the `replaceChildren` helper to wrap each child node in an extra `<div>`:
102+
103+
<<< @/examples/wrapping-list.vue
104+
105+
This might then be used something like this:
106+
107+
<<< @/examples/wrap-children-example.vue
108+
109+
<live-example>
110+
<wrap-children-example />
111+
</live-example>
112+
113+
## Inserting between children
114+
115+
The `betweenChildren` helper can be used to insert separators between children. For example:
116+
117+
<<< @/examples/insert-separators.js
118+
119+
With usage:
120+
121+
<<< @/examples/separators-example.vue
122+
123+
<style>
124+
.hr-example hr {
125+
border-top-color: #777;
126+
border-top-width: 2px;
127+
}
128+
</style>
129+
130+
<live-example class="hr-example">
131+
<separators-example />
132+
</live-example>
133+
134+
## Checking for empty content
135+
136+
The `isEmpty` helper can help to check whether the child VNodes are empty. Fragment nodes aren't counted as content, neither are comments nor strings of collapsible whitespace.
137+
138+
<<< @/examples/results-list.js
139+
140+
Example usage, with an empty list followed by a non-empty list:
141+
142+
<<< @/examples/empty-list-example.vue
143+
144+
<live-example>
145+
<empty-list-example />
146+
</live-example>
147+
148+
See it on the SFC Playground: [Composition API](https://sfc.vuejs.org/#eNqdU01v2zAM/SuEe3ACxNZ2zdxkO+w27LBrXQyJRCdqZEnQh5ci8H8fZbtp0iLD0JOkR/JRfCRP2Tdryy5itswqz520ATyGaFe1lq01LsAv9FEF/0P6AI0zLeQlu8DKJ5/XumJjMIXRI2Br1SYgvQAq63D18FixdA6AG6MLReEDQpiS0BWNcfd1NppBanh4rLPV6QQT0vcVU3LkYO9Ixjy5Q5EvIN85RJ0uWxUx/1j2G1z//aeKXQhBTx+eFYLnxqIghH4EpxS43fDDzpmoxRLuEPHLABon0C3hsz2CN0oKuOOcDya7EULqHdk+2SMh/aB/IifWbJGNjSvajaXmGE2tHdLUk8HX2XJMnDDqfXrX2T4E65eM+YangXjypXE7RrfSRR1kiyX6ttg688ejI+I6W7xwfPUH6YLC5F102ggsYpBqSPRKHLU97EpuWnbLnwkS7i14lVZgd52anBkZOnSFQ02KoftXOW9c35WUaEnOnlS8nnEScdqHE+yhnzaBiGn4zwbpv7c2PJ/Nt+qkmFrjcYgS2GzSIDVR8yCNvty32e8FsXplgod+PvaMG02LyPdSCaoC7kczCTPQrMvZHNbrtDmUAkA2MJt+NXuJmU9MQDMcotOwn+VCdmnAf5pprn0+n6RIx6tfVOR2JhpGL+v/AratesI=) | [Options API](https://sfc.vuejs.org/#eNqdU8GO2jAQ/ZWR9xCQSNxeaRbaQ29VD71uVhXEE/Di2JbtpKxQ/r3jJGRhEVW1p8Rvxm/s5/dO7Ju1WdsgW7Lcl07asCq0rK1xAX6hb1TwP6QPUDlTQ5LxCyx78UmhAQqNx75fYLWhIpwiWhoi0aiDXw4AXPJFoCt0F3fnfBpMi4C1VZuAtALIrcPV03PO47cH3MCRKiLpEcKUhDatjHss2FAGqeHpuWCr0wlGpOtyruTAwW9IhjmJQ5EsINk5RB1/tqrB5GPT73D995lyfiEELX14VQi+NBYFIXSiQdXtpjzsnGm0WMIDIn7pQeMEuiV8tkfwRkkBD2VZ9iW7EULqHdU+2SMh9AikfyQnVrZgw9On9cbS8xpNtujHFGPBF2x6zoKRb+K6YPsQrF9y7qsymunFZ8btOP1lrtFB1pihr9OtM388OiIu2OLM8dUfpAsKY3faaiMwbYJU/aA34kbbwy4jT/F7/VyQcO/Bq7EC2+vR1Myp0KJLHWpSDN2/rvOu9eZKZ0+TitcpIRHHRJ1gD92YJSKm+EwF6b/XNrxO5Xv3pD03gasaXQZp9GXCZr8XxOqVCR66+TmTmqJc7qUSdAt4HMokTE+zzmZzWK9jcmgEgKxgNp5qdt4zH5mAPBwap2E/S4Rso8F/mtHXPpmPUsTPW1+jqG0i6q3Hur9YTo+S)
149+
150+
## Adding a `ref` to a slot
151+
152+
Adding a 'template ref' to slot content can be a bit tricky. A common trick is to add the `ref` attribute to a surrounding element and then walk the DOM tree to access the relevant element. But that only works if there is a surrounding element in the relevant component, and it also only allows access to elements, not components.
153+
154+
There are a number of ways we might attempt to implement something like this with `vue-vnode-utils`.
155+
156+
Let's assume our component only allows a single child inside the slot, a bit like a `<Transition>` component. Let's further assume that we want to skip fragment nodes, comments and empty text nodes. We could use `extractSingleChild()`, which will pull out a single element or component node, with a console warning if multiple nodes are found.
157+
158+
<<< @/examples/add-ref.js
159+
160+
The example is using `outerHTML` during rendering, which is only there so we can see the contents of the `ref`. Something like that should not appear in real code.
161+
162+
Usage might look something like this:
163+
164+
<<< @/examples/add-slot-ref-example.vue
165+
166+
This example is a bit silly, because in practice you're very unlikely to use a `v-for` in a scenario where only one node can be rendered. But it shows how `extractSingleChild()` successfully negotiates the `v-for` fragments and `v-if` comment nodes.
167+
168+
One quirk of this example is the position of the `key`. There's no benefit in having a `key` on the nodes we discard, as they'll never make it to the patching process anyway. Instead, we need the `key` to be placed on the `<div>` VNode that we keep. This will ensure that the `<div>` DOM nodes are not reused for different items, updating the `ref` on each rendering update.
169+
170+
In a real use case we'd probably be fine with reusing the same `<div>`, so the `key` could be omitted.
171+
172+
<style>
173+
.button-example button {
174+
background-color: #ccc;
175+
border: 1px solid #777;
176+
border-radius: 3px;
177+
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.25);
178+
color: #000;
179+
padding: 2px 5px;
180+
}
181+
</style>
182+
183+
<live-example class="button-example">
184+
<add-slot-ref-example />
185+
</live-example>
186+
187+
Another way we might approach this is using `addProp()`. This could be used to add a `ref` to a single node, like in the previous example, or it could handle the more general case with multiple top-level nodes:
188+
189+
<<< @/examples/add-multiple-refs.js
190+
191+
As with the previous example, this example contains a circularity because it is rendering the `outerHTML` of the `ref` elements, but that's just for demo purposes and in real code you would do whatever you need to do with the elements/components, not just dump out their `outerHTML`.
192+
193+
Usage might be something like this:
194+
195+
<<< @/examples/add-slot-refs-example.vue
196+
197+
All of which gives:
198+
199+
<live-example class="button-example">
200+
<add-slot-refs-example />
201+
</live-example>
202+
203+
See it on the SFC Playground: [Composition API](https://sfc.vuejs.org/#eNp9VVtv0zAU/itHAampaJIixEvoOhAvIDEJId6WiWXJaePNsS3bzTqV8Ns5di5Lu0uf7HM+f9+5pofgi1Jxs8MgDVam0ExZMGh3ap0JViupLRxA4wZa2GhZw4ygs9H1pSwvdtwyxfEXbkwPiZMTe3xr6E0mCimMhULuhIUzxxp+mGdilXS6pEgXi7XiuUW6AaxudtZKAZ8Lzoq7syzwb9+9y4I1SaySzu2hf1/Gk9ZFbqu4zvdhZ4jg/QKWc6L5hbVs8IhplZdlVPfhRxSl8WZyDLFBE22kJnoBTHT5EFUHIljJmvV3wsLhAALadpU4S8+RHCeYPCc2AQWLoKt1VOeK6igFNergnma9w2RBCt7ibNQed8+Cylpl0iQxm8K199bEUm8TOsWawmU1xmjq6EbLe4OaiLNgMXB8NndMW44OHTVClhjtLONe6JF4J9TdNi5knbyEp7SNPTUeyZbYHEsTOCFHg5pqIUrUqF9L5wT6JCVH22aipSo+HUkq5Djh1YLGMS8sa/D5ST8ANeqnlsqM/pfS9qOOe/+sxE1Oql1//F6FfxZEZri0xDQfGpck8LtiBnKt8we4Z5xDJXkJtkKwUkUcG+SAHGsU1iRUdSWFO47P+xqUcPPgXzmFztlvXcV46ZfUbV6Xanh5RfvXoTj2mK9+nHurppC1gHAOZ+shVmIccUS2HLBHShQO+YaahT5farevxnlMfOfncHm1OGUeOGgRvlM+e+J4FKO9H6XG0CZPnW2TQshoe05p3Y9tBt+Jo8/IVedyUL4iaQc+RrbUA4OeaXwScxRbW8F6EuprEgN+mtqJyvQ6uYzHdmzbWIfLSQn7BvRb5X5VOFMaZ4spDOBa7izqb78vfqRZJq4n+Gm49OEJj+MLkS+A+RJfvz2wNoW3B+TxyNZeT/Hz+FYyEc5IYkZxD2Y3ev2pO/js3LZmgvbV2AeOJi6M21OKvSvoTV7cbTVVrEzhDSJ+8kapafRTeK/2YCRnJbwpisK7FE0gE9sUPqo9GYg6E/1/xD+gr3LP+pRguVx6gjrXWyYi2kFyLz2JM+6je1baimwfB+OpVND+BzRxXJ0=) | [Options API](https://sfc.vuejs.org/#eNqFVdtO4zAQ/ZVRQGoRTVK04iVbuqB9WaRFWiHeCBIhmTYGx45stxSV7Lfv2Lk0aYFFPNhnZs7cfNKtd1WWwXqFXuTNdKpYaeaxYEUplYGrLLtZccNKjre40LBQsoBREO7hwbMexQIgFrhxcRkuErLD1qKpJDKBwuioBmCf14LVpGYAyBKTjE9aV4VmpUR7s2wrYSL4Vt8rFxoL+p+FXfV0MViUPDFIN4DZ08oYKeAy5Sx9uYg9R3J6GntzqmQW1mbn+v65P1zATWLyoEg24xrw4WwC0xOiucVCrnHANEuyzC+aLn1FbTqYDG1tsPYXUhG9ACbqxoiqbXSWsfX8mnxhuwUBVTULLdJwhMMGw4+S9Zy8iVev1C+SktYlBa3bzTRuDDr2uvXEHr0He4+93JhSR2GoF6l9JM86kGoZ0ilQVC4rMEBd+E9KvmpURBx7bo+W41K/MGU4Wm9/LWSG/sow7hLtiFeifFkG9EbCz/ypbW32wUHaDNfD1OQckmGNimYhMlSovmpnz/WgpfaR0RQPXz4NslHLFnKoGo0QOUmiM9B6/ihZ6s7+WbMU9LGKvlZFGMJdzjQkSiVv8Mo4h1zyDEyOYGTpc1wjB+RYWB2GO0n2CJr2M3h6c3GaS9OpLmc8sw1HcP/QjLlBf9aCnPYF2Wm55tzVzdH0okhRFFZbUil0Y6MgsrQjGxtqLDi21WjatBvJj4Ao399tLUCni3n/+2B5SAHXlHlDPLt0JPgm2eEALbKIYMxIMkNC+8cWrWUAA82JauuGc9+mfaC81r/vXNH4NTqqYVTAUSxNDvMem6v3P9nauIv9uEHW3aU7NofqpB3HXvxgaI2k+q/ufrB/2lb3IPLxqFQ4muxcAB7lyqD6dXfzO4pj8dj5HrRDX6Zxv/Qx8gkwt4zH4y2rIjjeIg86uupx530SPEsmxiPKMKK2avChOT309KvNG0fKqq1uqdZ6xE9J+rJU1G4WwREifnegVPR2IzgrN6AlZxkcpWnqTCU9TiaWEZyXGwKIOxbNb8ZfoK90w3pIMJ1OHUGRqCUTPkmTzFNHYsGN/8oykxN23oL7qbzqHxvhfis=)

docs/examples/add-class-example.vue

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup>
2+
import AddOutline from './add-outline'
3+
</script>
4+
5+
<template>
6+
<add-outline>
7+
<div>Top</div>
8+
<template v-for="n in 3">
9+
<div>{{ n }} - Upper</div>
10+
<div>{{ n }} - Lower</div>
11+
</template>
12+
<div>Bottom</div>
13+
</add-outline>
14+
</template>

docs/examples/add-multiple-refs.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { h, reactive } from 'vue'
2+
import { addProps } from '@skirtle/vue-vnode-utils'
3+
4+
export default {
5+
setup(_, { slots }) {
6+
// This array will hold the top-level elements/components
7+
// rendered by the slot
8+
const childRefs = reactive([])
9+
10+
let childCount
11+
12+
return () => {
13+
childCount = 0
14+
15+
const children = addProps(slots.default(), () => {
16+
const refIndex = childCount++
17+
18+
return {
19+
ref: (item) => {
20+
if (item) {
21+
childRefs[refIndex] = item
22+
} else if (childRefs.length > childCount) {
23+
childRefs.length = childCount
24+
}
25+
}
26+
}
27+
})
28+
29+
return [
30+
children,
31+
h('pre', [
32+
`outerHTML:\n`,
33+
childRefs.map(
34+
(el, i) => `${i}: ${el.outerHTML}`
35+
).join('\n')
36+
])
37+
]
38+
}
39+
}
40+
}

docs/examples/add-outline.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { h } from 'vue'
2+
import { addProps } from '@skirtle/vue-vnode-utils'
3+
4+
export default function AddOutline(_, { slots }) {
5+
const children = addProps(slots.default(), () => {
6+
return {
7+
class: 'child-outline'
8+
}
9+
})
10+
11+
return h('div', children)
12+
}

docs/examples/add-ref.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { cloneVNode, h, ref } from 'vue'
2+
import { extractSingleChild } from '@skirtle/vue-vnode-utils'
3+
4+
export default {
5+
setup(_, { slots }) {
6+
const rootRef = ref(null)
7+
8+
return () => {
9+
const child = extractSingleChild(slots.default())
10+
11+
// Pass `true` to avoid overriding any existing ref
12+
const clone = cloneVNode(child, { ref: rootRef }, true)
13+
14+
return [
15+
clone,
16+
h('pre', `outerHTML: ${ rootRef.value?.outerHTML }`)
17+
]
18+
}
19+
}
20+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script setup>
2+
import { ref } from 'vue'
3+
import AddRef from './add-ref'
4+
5+
const index = ref(1)
6+
</script>
7+
8+
<template>
9+
<button @click="index = index % 3 + 1">Next</button>
10+
<add-ref>
11+
<template v-for="n in 3">
12+
<template v-if="n === index">
13+
<div :key="n">Item {{ n }}</div>
14+
</template>
15+
</template>
16+
</add-ref>
17+
</template>

0 commit comments

Comments
 (0)