I want to create a Vue component <Steps></Steps
that is an ordered list, but provides some flexibility in how the slots are passed in.
Each step, or <li>
, will be a heading, it could be either an h2
, h3
, or h4
.
Inside of the <Steps>
component, each time a heading tag is found, that needs to create and wrap everything up to the next heading tag of the same heading level. I am running into problems also because if a an h4
is used after an h3
this should not create an extra <li>
.
It seems the best way to solve this would be with Vue's render h()
function but I am really stuck on this one.
Input
<Steps>
<h3>heading a</h3>
<p>paragraph a<p>
<div class="code-block"><pre>const x = 1</pre></div>
<h4>this should not create an li</h4>
<h3>heading b</h3>
<p>paragraph b<p>
</Steps>
Output
<ol>
<li>
<h3>heading a</h3>
<p>paragraph a<p>
<div class="code-block"><pre>const x = 1</pre></div>
<h4>this should not create an li</h4>
</li>
<li>
<h3>heading b</h3>
<p>paragraph b<p>
</li>
</ol>
Another thing to note is that if there is one heading at a certain level, e.g. h3
, a smaller tag should not create a new li
.
Input
<Steps>
<h3>heading a</h3>
<p>paragraph a<p>
<div class="code-block"><pre>const x = 1</pre></div>
<h4>this should not create an li</h4>
<p>paragraph b<p>
</Steps>
Output
<ol>
<li>
<h3>heading a</h3>
<p>paragraph a<p>
<div class="code-block"><pre>const x = 1</pre></div>
<h4>this should not create an li</h4>
<p>paragraph b<p>
</li>
</ol>
You can do something like this (the first encountered header taken as a li
start):
import { h } from 'vue';
const headings = new Set(['h2', 'h3', 'h4'])
export default function Steps(props, {slots}){
const slotted = slots.default?.();
if(!slotted) return;
let type;
const out = [];
let curr;
for(let i = 0; i<slotted.length;i++){
const vnode = slotted[i];
if(headings.has(vnode.type)){
type ??= vnode.type;
if(vnode.type === type){
curr && out.push(curr);
curr = [vnode];
} else{
(curr ??= []).push(vnode);
}
}else{
(curr ??= []).push(vnode);
}
}
curr && out.push(curr);
return h('ol', {}, out.map(li => h('li', li)));
}