Skip to content
Advertisement

Open one accordion section at a time. (Svelte)

I’d like to ask someone who can figure out how to allow this accordion component to open only one section at a time.

It means that if a new accordion is open the previous one has to close automatically.

In the ideal situation, this function could be optional for specific accordions.

Thank you for your time.

Accordion.svelte

 <script>
    import { linear } from 'svelte/easing';
    import { slide} from 'svelte/transition';
    
    export let open = false;

    function handleClick() {
        open = !open
    } 
    
</script>

   <div  class="accordion">
      <div class="header" on:click={handleClick}>
          <div class="text">
              <slot name="head"></slot> 
          </div>
      </div>

      {#if open}
      <div class="details" transition:slide="{{duration: 500, easing: linear}}" >
          <slot name="details">
          </slot>
      </div>
      {/if}

  </div>

<style>
       div.accordion {
          margin: 1rem 0;
      }
      
      div.header {
          display:flex;
          width:100%;
      }
      
      div.header .text {
          flex: 1;
      }
      
      div.details {
          background-color: transparent;
          padding:1rem;
      }
  </style>

app.svelte

<script>

import Accordion from "./Accordion.svelte"

</script>

       <Accordion>
          <div slot="head">
              <h2>Test one</h2>
          </div>
          <div slot="details">
              <ul>
                <Accordion>
                    <div slot="head">
                      <h4>The test of subitem?</h4>
                    </div>
                    <div slot="details">
                        <li>1</li>
                        <li>2</li>
                        <li>3</li>
                    </div>
                </Accordion >

                <Accordion>
                    <div slot="head">
                        <h4>Test of subitem 2</h4>
                    </div>
                    <div slot="details">
                        <li>one</li>
                        <li>two</li>
                        <li>three</li>
                    </div>
                </Accordion>
              </ul>
          </div>
       </Accordion>

       <Accordion>
          <div slot="head">
              <h4>Second test</h4>
          </div>
          <div slot="details">
              <ul>
                  <li>again one</li>
                  <li>two again</li>
                  <li>three repeat</li>
              </ul>
          </div>
        </Accordion>


<style>
li {
    margin: 0;
    padding: 1em;
    text-align: left;
    list-style-type: none;
    cursor: pointer;
    color: black;
}

h4, h2 {
        cursor: pointer;
}
</style>

Advertisement

Answer

For this one would usually use a context. The context can then manage the current accordion (or a key representing it) or dispatch events to other accordion instances.

As an example of storing the current accordion:

import { setContext, getContext } from 'svelte';
import { writable } from 'svelte/store';

const key = {}; // Object instances are unique, hence useful for keys

export const getAccordionContext = () => getContext(key);
export const createAccordionContext = () => {
    const current = writable(null);
    const context = { current };
    setContext(key, context);
    
    return context;
}

If there is no wrapper component that is used in conjunction with the accordion items, a context has be created elsewhere (e.g. the App). Also, each accordion item should also create its own context, so if it contains items of its own, it will not be closed if a child is opened.

When an accordion is opened the current accordion of the context can be updated. A reactive statement can be used to close all other accordions that belong to a context.

// In Accordion.svelte
const { current } = getAccordionContext();
const currentKey = {}; // Object representing current accordion component

createAccordionContext(); // Context for children

function handleClick() {
    open = !open
    if (open)
        $current = currentKey;
}

$: if ($current != currentKey)
    open = false;

REPL example

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement