Files
sigpro-ui/docs/components_old/accordion.md
2026-04-02 19:31:39 +02:00

26 KiB
Raw Blame History

Accordion

Collapsible accordion component for organizing content into expandable sections with DaisyUI styling.

Tag

Accordion

Props

Prop Type Default Description
title string | VNode | Signal Required Accordion section title
open boolean | Signal<boolean> false Whether the accordion is expanded
name string - Group name for radio-style accordions (only one open at a time)
class string '' Additional CSS classes (DaisyUI + Tailwind)
children VNode | Array<VNode> Required Content to display when expanded

Live Examples

Basic Accordion

Live Demo

const BasicDemo = () => {
  const open1 = $(false);
  const open2 = $(false);
  const open3 = $(false);
  
  return Div({ class: 'flex flex-col gap-2' }, [
    Accordion({
      title: 'Section 1',
      open: open1,
      onclick: () => open1(!open1())
    }, [
      Div({ class: 'p-2' }, 'Content for section 1. This is a basic accordion section.')
    ]),
    Accordion({
      title: 'Section 2',
      open: open2,
      onclick: () => open2(!open2())
    }, [
      Div({ class: 'p-2' }, 'Content for section 2. You can put any content here.')
    ]),
    Accordion({
      title: 'Section 3',
      open: open3,
      onclick: () => open3(!open3())
    }, [
      Div({ class: 'p-2' }, 'Content for section 3. Accordions are great for FAQs.')
    ])
  ]);
};
$mount(BasicDemo, '#demo-basic');

Group Accordion (Radio Style)

Live Demo

const GroupDemo = () => {
  const openSection = $('section1');
  
  return Div({ class: 'flex flex-col gap-2' }, [
    Accordion({
      title: 'Section 1',
      name: 'group',
      open: () => openSection() === 'section1',
      onclick: () => openSection('section1')
    }, [
      Div({ class: 'p-2' }, 'Content for section 1. Only one section can be open at a time.')
    ]),
    Accordion({
      title: 'Section 2',
      name: 'group',
      open: () => openSection() === 'section2',
      onclick: () => openSection('section2')
    }, [
      Div({ class: 'p-2' }, 'Content for section 2. Opening this will close section 1.')
    ]),
    Accordion({
      title: 'Section 3',
      name: 'group',
      open: () => openSection() === 'section3',
      onclick: () => openSection('section3')
    }, [
      Div({ class: 'p-2' }, 'Content for section 3. This is useful for FAQ sections.')
    ])
  ]);
};
$mount(GroupDemo, '#demo-group');

FAQ Accordion

Live Demo

const FaqDemo = () => {
  const openFaq = $('faq1');
  
  const faqs = [
    { id: 'faq1', question: 'What is this component?', answer: 'This is an accordion component built with DaisyUI and Tailwind CSS for creating collapsible content sections.' },
    { id: 'faq2', question: 'How do I use it?', answer: 'Simply import the Accordion component and pass title and children props. Use the open prop to control expansion.' },
    { id: 'faq3', question: 'Can I have multiple open?', answer: 'Yes! By default, accordions can be opened independently. Use the name prop to create groups where only one can be open.' },
    { id: 'faq4', question: 'Is it accessible?', answer: 'Yes, the accordion uses proper ARIA attributes and keyboard navigation support.' }
  ];
  
  return Div({ class: 'flex flex-col gap-2' }, faqs.map(faq => 
    Accordion({
      title: faq.question,
      name: 'faq-group',
      open: () => openFaq() === faq.id,
      onclick: () => openFaq(openFaq() === faq.id ? '' : faq.id)
    }, [
      Div({ class: 'p-2 text-sm' }, faq.answer)
    ])
  ));
};
$mount(FaqDemo, '#demo-faq');

With Rich Content

Live Demo

const RichDemo = () => {
  const open1 = $(true);
  const open2 = $(false);
  
  return Div({ class: 'flex flex-col gap-2' }, [
    Accordion({
      title: Span({ class: 'flex items-center gap-2' }, ['📊', 'Statistics']),
      open: open1,
      onclick: () => open1(!open1())
    }, [
      Div({ class: 'p-2' }, [
        Div({ class: 'grid grid-cols-2 gap-4' }, [
          Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
            Div({ class: 'stat-title' }, 'Users'),
            Div({ class: 'stat-value text-lg' }, '1,234')
          ]),
          Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
            Div({ class: 'stat-title' }, 'Revenue'),
            Div({ class: 'stat-value text-lg' }, '$45,678')
          ]),
          Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
            Div({ class: 'stat-title' }, 'Growth'),
            Div({ class: 'stat-value text-lg text-success' }, '+23%')
          ]),
          Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
            Div({ class: 'stat-title' }, 'Active'),
            Div({ class: 'stat-value text-lg' }, '89%')
          ])
        ])
      ])
    ]),
    Accordion({
      title: Span({ class: 'flex items-center gap-2' }, ['👥', 'Team Members']),
      open: open2,
      onclick: () => open2(!open2())
    }, [
      Div({ class: 'p-2 space-y-2' }, [
        Div({ class: 'flex items-center gap-3 p-2 hover:bg-base-100 rounded-lg' }, [
          Div({ class: 'avatar placeholder' }, [
            Div({ class: 'bg-primary text-primary-content rounded-full w-10 h-10 flex items-center justify-center' }, 'JD')
          ]),
          Div({ class: 'flex-1' }, [
            Div({ class: 'font-medium' }, 'John Doe'),
            Div({ class: 'text-sm opacity-70' }, 'Developer')
          ])
        ]),
        Div({ class: 'flex items-center gap-3 p-2 hover:bg-base-100 rounded-lg' }, [
          Div({ class: 'avatar placeholder' }, [
            Div({ class: 'bg-secondary text-secondary-content rounded-full w-10 h-10 flex items-center justify-center' }, 'JS')
          ]),
          Div({ class: 'flex-1' }, [
            Div({ class: 'font-medium' }, 'Jane Smith'),
            Div({ class: 'text-sm opacity-70' }, 'Designer')
          ])
        ])
      ])
    ])
  ]);
};
$mount(RichDemo, '#demo-rich');

Form Accordion

Live Demo

const FormAccordion = () => {
  const openStep = $('step1');
  const formData = $({
    name: '',
    email: '',
    address: '',
    payment: 'credit'
  });
  
  const updateField = (field, value) => {
    formData({ ...formData(), [field]: value });
  };
  
  const nextStep = () => {
    if (openStep() === 'step1') openStep('step2');
    else if (openStep() === 'step2') openStep('step3');
  };
  
  const prevStep = () => {
    if (openStep() === 'step2') openStep('step1');
    else if (openStep() === 'step3') openStep('step2');
  };
  
  const handleSubmit = () => {
    Toast('Form submitted!', 'alert-success', 2000);
    console.log(formData());
  };
  
  return Div({ class: 'flex flex-col gap-2' }, [
    Accordion({
      title: Span({ class: 'flex items-center gap-2' }, ['1⃣', 'Personal Information']),
      name: 'form-steps',
      open: () => openStep() === 'step1',
      onclick: () => openStep('step1')
    }, [
      Div({ class: 'p-4 space-y-4' }, [
        Input({
          label: 'Full Name',
          value: () => formData().name,
          placeholder: 'Enter your name',
          oninput: (e) => updateField('name', e.target.value)
        }),
        Input({
          label: 'Email',
          type: 'email',
          value: () => formData().email,
          placeholder: 'email@example.com',
          oninput: (e) => updateField('email', e.target.value)
        }),
        Div({ class: 'flex justify-end mt-2' }, [
          Button({ 
            class: 'btn btn-primary btn-sm',
            onclick: nextStep,
            disabled: () => !formData().name || !formData().email
          }, 'Next →')
        ])
      ])
    ]),
    Accordion({
      title: Span({ class: 'flex items-center gap-2' }, ['2⃣', 'Address']),
      name: 'form-steps',
      open: () => openStep() === 'step2',
      onclick: () => openStep('step2')
    }, [
      Div({ class: 'p-4 space-y-4' }, [
        Input({
          label: 'Address',
          value: () => formData().address,
          placeholder: 'Street address',
          oninput: (e) => updateField('address', e.target.value)
        }),
        Div({ class: 'flex justify-between mt-2' }, [
          Button({ class: 'btn btn-ghost btn-sm', onclick: prevStep }, '← Back'),
          Button({ 
            class: 'btn btn-primary btn-sm',
            onclick: nextStep
          }, 'Next →')
        ])
      ])
    ]),
    Accordion({
      title: Span({ class: 'flex items-center gap-2' }, ['3⃣', 'Payment']),
      name: 'form-steps',
      open: () => openStep() === 'step3',
      onclick: () => openStep('step3')
    }, [
      Div({ class: 'p-4 space-y-4' }, [
        Div({ class: 'flex flex-col gap-2' }, [
          Radio({
            label: 'Credit Card',
            value: () => formData().payment,
            radioValue: 'credit',
            onclick: () => updateField('payment', 'credit')
          }),
          Radio({
            label: 'PayPal',
            value: () => formData().payment,
            radioValue: 'paypal',
            onclick: () => updateField('payment', 'paypal')
          }),
          Radio({
            label: 'Bank Transfer',
            value: () => formData().payment,
            radioValue: 'bank',
            onclick: () => updateField('payment', 'bank')
          })
        ]),
        Div({ class: 'flex justify-between mt-2' }, [
          Button({ class: 'btn btn-ghost btn-sm', onclick: prevStep }, '← Back'),
          Button({ class: 'btn btn-success btn-sm', onclick: handleSubmit }, 'Submit')
        ])
      ])
    ])
  ]);
};
$mount(FormAccordion, '#demo-form');

All Variants

Live Demo

const VariantsDemo = () => {
  const open1 = $(true);
  const open2 = $(false);
  const open3 = $(false);
  
  return Div({ class: 'flex flex-col gap-4' }, [
    Div({ class: 'text-sm font-bold' }, 'Default Accordion'),
    Div({ class: 'flex flex-col gap-2' }, [
      Accordion({ title: 'Default style', open: open1, onclick: () => open1(!open1()) }, [
        Div({ class: 'p-2' }, 'Default accordion with standard styling.')
      ])
    ]),
    
    Div({ class: 'text-sm font-bold mt-2' }, 'Custom Styling'),
    Div({ class: 'flex flex-col gap-2' }, [
      Accordion({ 
        title: Span({ class: 'text-primary font-bold' }, 'Primary Title'),
        open: open2,
        onclick: () => open2(!open2()),
        class: 'bg-primary/5 border-primary/20'
      }, [
        Div({ class: 'p-2' }, 'Accordion with custom styling and primary color.')
      ])
    ]),
    
    Div({ class: 'text-sm font-bold mt-2' }, 'With Icons'),
    Div({ class: 'flex flex-col gap-2' }, [
      Accordion({ 
        title: Span({ class: 'flex items-center gap-2' }, ['✨', 'Featured Content']),
        open: open3,
        onclick: () => open3(!open3())
      }, [
        Div({ class: 'p-2' }, 'Accordion with emoji icons in the title.')
      ])
    ])
  ]);
};
$mount(VariantsDemo, '#demo-variants');
<script> (function() { const initAccordionExamples = () => { // 1. Basic Accordion const basicTarget = document.querySelector('#demo-basic'); if (basicTarget && !basicTarget.hasChildNodes()) { const BasicDemo = () => { const open1 = $(false); const open2 = $(false); const open3 = $(false); return Div({ class: 'flex flex-col gap-2' }, [ Accordion({ title: 'Section 1', open: open1, onclick: () => open1(!open1()) }, [ Div({ class: 'p-2' }, 'Content for section 1. This is a basic accordion section.') ]), Accordion({ title: 'Section 2', open: open2, onclick: () => open2(!open2()) }, [ Div({ class: 'p-2' }, 'Content for section 2. You can put any content here.') ]), Accordion({ title: 'Section 3', open: open3, onclick: () => open3(!open3()) }, [ Div({ class: 'p-2' }, 'Content for section 3. Accordions are great for FAQs.') ]) ]); }; $mount(BasicDemo, basicTarget); } // 2. Group Accordion (Radio Style) const groupTarget = document.querySelector('#demo-group'); if (groupTarget && !groupTarget.hasChildNodes()) { const GroupDemo = () => { const openSection = $('section1'); return Div({ class: 'flex flex-col gap-2' }, [ Accordion({ title: 'Section 1', name: 'group', open: () => openSection() === 'section1', onclick: () => openSection('section1') }, [ Div({ class: 'p-2' }, 'Content for section 1. Only one section can be open at a time.') ]), Accordion({ title: 'Section 2', name: 'group', open: () => openSection() === 'section2', onclick: () => openSection('section2') }, [ Div({ class: 'p-2' }, 'Content for section 2. Opening this will close section 1.') ]), Accordion({ title: 'Section 3', name: 'group', open: () => openSection() === 'section3', onclick: () => openSection('section3') }, [ Div({ class: 'p-2' }, 'Content for section 3. This is useful for FAQ sections.') ]) ]); }; $mount(GroupDemo, groupTarget); } // 3. FAQ Accordion const faqTarget = document.querySelector('#demo-faq'); if (faqTarget && !faqTarget.hasChildNodes()) { const FaqDemo = () => { const openFaq = $('faq1'); const faqs = [ { id: 'faq1', question: 'What is this component?', answer: 'This is an accordion component built with DaisyUI and Tailwind CSS for creating collapsible content sections.' }, { id: 'faq2', question: 'How do I use it?', answer: 'Simply import the Accordion component and pass title and children props. Use the open prop to control expansion.' }, { id: 'faq3', question: 'Can I have multiple open?', answer: 'Yes! By default, accordions can be opened independently. Use the name prop to create groups where only one can be open.' }, { id: 'faq4', question: 'Is it accessible?', answer: 'Yes, the accordion uses proper ARIA attributes and keyboard navigation support.' } ]; return Div({ class: 'flex flex-col gap-2' }, faqs.map(faq => Accordion({ title: faq.question, name: 'faq-group', open: () => openFaq() === faq.id, onclick: () => openFaq(openFaq() === faq.id ? '' : faq.id) }, [ Div({ class: 'p-2 text-sm' }, faq.answer) ]) )); }; $mount(FaqDemo, faqTarget); } // 4. With Rich Content const richTarget = document.querySelector('#demo-rich'); if (richTarget && !richTarget.hasChildNodes()) { const RichDemo = () => { const open1 = $(true); const open2 = $(false); return Div({ class: 'flex flex-col gap-2' }, [ Accordion({ title: Span({ class: 'flex items-center gap-2' }, ['📊', 'Statistics']), open: open1, onclick: () => open1(!open1()) }, [ Div({ class: 'p-2' }, [ Div({ class: 'grid grid-cols-2 gap-4' }, [ Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [ Div({ class: 'stat-title' }, 'Users'), Div({ class: 'stat-value text-lg' }, '1,234') ]), Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [ Div({ class: 'stat-title' }, 'Revenue'), Div({ class: 'stat-value text-lg' }, '$45,678') ]), Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [ Div({ class: 'stat-title' }, 'Growth'), Div({ class: 'stat-value text-lg text-success' }, '+23%') ]), Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [ Div({ class: 'stat-title' }, 'Active'), Div({ class: 'stat-value text-lg' }, '89%') ]) ]) ]) ]), Accordion({ title: Span({ class: 'flex items-center gap-2' }, ['👥', 'Team Members']), open: open2, onclick: () => open2(!open2()) }, [ Div({ class: 'p-2 space-y-2' }, [ Div({ class: 'flex items-center gap-3 p-2 hover:bg-base-100 rounded-lg' }, [ Div({ class: 'avatar placeholder' }, [ Div({ class: 'bg-primary text-primary-content rounded-full w-10 h-10 flex items-center justify-center' }, 'JD') ]), Div({ class: 'flex-1' }, [ Div({ class: 'font-medium' }, 'John Doe'), Div({ class: 'text-sm opacity-70' }, 'Developer') ]) ]), Div({ class: 'flex items-center gap-3 p-2 hover:bg-base-100 rounded-lg' }, [ Div({ class: 'avatar placeholder' }, [ Div({ class: 'bg-secondary text-secondary-content rounded-full w-10 h-10 flex items-center justify-center' }, 'JS') ]), Div({ class: 'flex-1' }, [ Div({ class: 'font-medium' }, 'Jane Smith'), Div({ class: 'text-sm opacity-70' }, 'Designer') ]) ]) ]) ]) ]); }; $mount(RichDemo, richTarget); } // 5. Form Accordion const formTarget = document.querySelector('#demo-form'); if (formTarget && !formTarget.hasChildNodes()) { const FormAccordion = () => { const openStep = $('step1'); const formData = $({ name: '', email: '', address: '', payment: 'credit' }); const updateField = (field, value) => { formData({ ...formData(), [field]: value }); }; const nextStep = () => { if (openStep() === 'step1') openStep('step2'); else if (openStep() === 'step2') openStep('step3'); }; const prevStep = () => { if (openStep() === 'step2') openStep('step1'); else if (openStep() === 'step3') openStep('step2'); }; const handleSubmit = () => { Toast('Form submitted!', 'alert-success', 2000); console.log(formData()); }; return Div({ class: 'flex flex-col gap-2' }, [ Accordion({ title: Span({ class: 'flex items-center gap-2' }, ['1', 'Personal Information']), name: 'form-steps', open: () => openStep() === 'step1', onclick: () => openStep('step1') }, [ Div({ class: 'p-4 space-y-4' }, [ Input({ label: 'Full Name', value: () => formData().name, placeholder: 'Enter your name', oninput: (e) => updateField('name', e.target.value) }), Input({ label: 'Email', type: 'email', value: () => formData().email, placeholder: 'email@example.com', oninput: (e) => updateField('email', e.target.value) }), Div({ class: 'flex justify-end mt-2' }, [ Button({ class: 'btn btn-primary btn-sm', onclick: nextStep, disabled: () => !formData().name || !formData().email }, 'Next →') ]) ]) ]), Accordion({ title: Span({ class: 'flex items-center gap-2' }, ['2', 'Address']), name: 'form-steps', open: () => openStep() === 'step2', onclick: () => openStep('step2') }, [ Div({ class: 'p-4 space-y-4' }, [ Input({ label: 'Address', value: () => formData().address, placeholder: 'Street address', oninput: (e) => updateField('address', e.target.value) }), Div({ class: 'flex justify-between mt-2' }, [ Button({ class: 'btn btn-ghost btn-sm', onclick: prevStep }, '← Back'), Button({ class: 'btn btn-primary btn-sm', onclick: nextStep }, 'Next →') ]) ]) ]), Accordion({ title: Span({ class: 'flex items-center gap-2' }, ['3', 'Payment']), name: 'form-steps', open: () => openStep() === 'step3', onclick: () => openStep('step3') }, [ Div({ class: 'p-4 space-y-4' }, [ Div({ class: 'flex flex-col gap-2' }, [ Radio({ label: 'Credit Card', value: () => formData().payment, radioValue: 'credit', onclick: () => updateField('payment', 'credit') }), Radio({ label: 'PayPal', value: () => formData().payment, radioValue: 'paypal', onclick: () => updateField('payment', 'paypal') }), Radio({ label: 'Bank Transfer', value: () => formData().payment, radioValue: 'bank', onclick: () => updateField('payment', 'bank') }) ]), Div({ class: 'flex justify-between mt-2' }, [ Button({ class: 'btn btn-ghost btn-sm', onclick: prevStep }, '← Back'), Button({ class: 'btn btn-success btn-sm', onclick: handleSubmit }, 'Submit') ]) ]) ]) ]); }; $mount(FormAccordion, formTarget); } // 6. All Variants const variantsTarget = document.querySelector('#demo-variants'); if (variantsTarget && !variantsTarget.hasChildNodes()) { const VariantsDemo = () => { const open1 = $(true); const open2 = $(false); const open3 = $(false); return Div({ class: 'flex flex-col gap-4' }, [ Div({ class: 'text-sm font-bold' }, 'Default Accordion'), Div({ class: 'flex flex-col gap-2' }, [ Accordion({ title: 'Default style', open: open1, onclick: () => open1(!open1()) }, [ Div({ class: 'p-2' }, 'Default accordion with standard styling.') ]) ]), Div({ class: 'text-sm font-bold mt-2' }, 'Custom Styling'), Div({ class: 'flex flex-col gap-2' }, [ Accordion({ title: Span({ class: 'text-primary font-bold' }, 'Primary Title'), open: open2, onclick: () => open2(!open2()), class: 'bg-primary/5 border-primary/20' }, [ Div({ class: 'p-2' }, 'Accordion with custom styling and primary color.') ]) ]), Div({ class: 'text-sm font-bold mt-2' }, 'With Icons'), Div({ class: 'flex flex-col gap-2' }, [ Accordion({ title: Span({ class: 'flex items-center gap-2' }, ['', 'Featured Content']), open: open3, onclick: () => open3(!open3()) }, [ Div({ class: 'p-2' }, 'Accordion with emoji icons in the title.') ]) ]) ]); }; $mount(VariantsDemo, variantsTarget); } }; initAccordionExamples(); if (window.$docsify) { window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => { hook.doneEach(initAccordionExamples); }); } })(); </script>