admin管理员组

文章数量:1277323

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>

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>
Share Improve this question edited Feb 25 at 11:35 dbzx10299 asked Feb 25 at 10:17 dbzx10299dbzx10299 1,1064 silver badges20 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

You can do something like this (the first encountered header taken as a li start):

Playground

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)));

}

本文标签: javascriptDynamically wrap and group headings in list items with Vue render functionsStack Overflow