Skip to content

Commit

Permalink
✨ Improve customizability of Accordion component
Browse files Browse the repository at this point in the history
  • Loading branch information
Frontendland committed Oct 18, 2024
1 parent 91f0389 commit 4ddf4ff
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 32 deletions.
52 changes: 32 additions & 20 deletions src/components/Accordion/Accordion.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,56 @@
import type { AccordionProps } from './accordion'
import ArrowDown from '../../icons/arrow-down.svg?raw'
import Plus from '../../icons/plus.svg?raw'
import styles from './accordion.module.scss'
interface Props extends AccordionProps {}
const {
items
items,
icon,
reverse,
className
} = Astro.props
const classes = [
styles.accordion,
reverse && styles.reverse,
icon === 'plus' && styles.plus,
className
]
---

<ul class={styles.accordion} data-id="w-accordion">
<ul class:list={classes} data-id="w-accordion">
{items.map((item: AccordionProps['items'][0]) => (
<li>
<button class={styles.title} data-toggle="true">
<button
class:list={[styles.title, item.reverse && styles.reverse]}
data-toggle="true"
>
{item.title}
<Fragment set:html={ArrowDown} />
{icon !== 'none' && (
<Fragment set:html={icon === 'plus' ? Plus : ArrowDown} />
)}
</button>
<div class={styles.wrapper}>
<div class={styles.content}>
<Fragment set:html={item.content} />
</div>
<div class={styles.content} set:html={item.content} />
</div>
</li>
))}
</ul>

<script>
const accordions = document.querySelectorAll('[data-id="w-accordion"]')

Array.from(accordions).forEach(element => {
element.addEventListener('click', event => {
const target = event.target as HTMLDivElement

if (target.dataset.toggle) {
target.dataset.open = target.dataset.open === 'true'
? 'false'
: 'true'
}
})
})
import { on } from '../../utils/DOMUtils'

on('[data-id="w-accordion"]', 'click', (event: Event) => {
const target = event.target as HTMLDivElement

if (target.dataset.toggle) {
target.dataset.open = target.dataset.open === 'true'
? 'false'
: 'true'
}
}, true)
</script>
26 changes: 22 additions & 4 deletions src/components/Accordion/Accordion.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
<script lang="ts">
import type { AccordionProps } from './accordion'
import { classNames } from '../../utils/classNames'
import ArrowDown from '../../icons/arrow-down.svg?raw'
import Plus from '../../icons/plus.svg?raw'
import styles from './accordion.module.scss'
export let items: AccordionProps['items']
export let items: AccordionProps['items'] = []
export let icon: AccordionProps['icon'] = null
export let reverse: AccordionProps['reverse'] = false
export let className: AccordionProps['className'] = ''
let state = Array(items.length).fill(false)
Expand All @@ -15,18 +21,30 @@
: state[i]
)
}
const classes = classNames([
styles.accordion,
reverse && styles.reverse,
icon === 'plus' && styles.plus,
className
])
</script>

<ul class={styles.accordion}>
<ul class={classes}>
{#each items as item, index}
<li>
<button
class={styles.title}
class={classNames([
styles.title,
item.reverse && styles.reverse
])}
data-open={state[index]}
on:click={() => toggle(index)}
>
{item.title}
{@html ArrowDown}
{#if icon !== 'none'}
{@html icon === 'plus' ? Plus : ArrowDown}
{/if}
</button>
<div class={styles.wrapper}>
<div class={styles.content}>
Expand Down
36 changes: 29 additions & 7 deletions src/components/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import React, { useState } from 'react'
import type { AccordionProps } from './accordion'

import { classNames } from '../../utils/classNames'

import ArrowDown from '../../icons/arrow-down.svg?raw'
import Plus from '../../icons/plus.svg?raw'

import styles from './accordion.module.scss'

const Accordion = ({ items }: AccordionProps) => {
const Accordion = ({
items,
icon,
reverse,
className
}: AccordionProps) => {
const [state, setState] = useState(Array(items.length).fill(false))

const toggle = (index: number) => {
Expand All @@ -15,20 +23,34 @@ const Accordion = ({ items }: AccordionProps) => {
))
}

const classes = classNames([
styles.accordion,
reverse && styles.reverse,
icon === 'plus' && styles.plus,
className
])

return (
<ul className={styles.accordion}>
<ul className={classes}>
{items.map((item, index) => (
<li key={index}>
<button
className={styles.title}
data-open={state[index]}
onClick={() => toggle(index)}
dangerouslySetInnerHTML={{ __html: `${item.title} ${ArrowDown}` }}
className={classNames([
styles.title,
item.reverse && styles.reverse
])}
dangerouslySetInnerHTML={{ __html: icon === 'none'
? item.title
: `${item.title} ${icon === 'plus' ? Plus : ArrowDown}`
}}
/>
<div className={styles.wrapper}>
<div className={styles.content}>
<div dangerouslySetInnerHTML={{ __html: item.content }} />
</div>
<div
className={styles.content}
dangerouslySetInnerHTML={{ __html: item.content }}
/>
</div>
</li>
))}
Expand Down
12 changes: 12 additions & 0 deletions src/components/Accordion/accordion.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
@include spacing(0);
list-style-type: none;

&.reverse .title {
@include layout(row-reverse, h-end);
}

&.plus .title[data-open="true"] svg {
transform: rotate(135deg);
}

li {
@include border(primary-50, bottom);
@include spacing(py-sm, px-none, m0);
Expand All @@ -27,6 +35,10 @@
background: transparent;
cursor: pointer;

&.reverse {
@include layout(row-reverse);
}

svg {
@include transition(transform);
@include size(15px);
Expand Down
4 changes: 4 additions & 0 deletions src/components/Accordion/accordion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@ export type AccordionProps = {
items: {
title: string
content: string
reverse?: boolean
}[]
icon?: 'plus' | 'none' | undefined | null
reverse?: boolean
className?: string
}
2 changes: 2 additions & 0 deletions src/components/Icon/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Home from '../../icons/home.svg?raw'
import Info from '../../icons/info.svg?raw'
import Moon from '../../icons/moon.svg?raw'
import Order from '../../icons/order.svg?raw'
import Plus from '../../icons/plus.svg?raw'
import Search from '../../icons/search.svg?raw'
import Sun from '../../icons/sun.svg?raw'
import Warning from '../../icons/warning.svg?raw'
Expand All @@ -33,6 +34,7 @@ const iconMap = {
'info': Info,
'moon': Moon,
'order': Order,
'plus': Plus,
'search': Search,
'sun': Sun,
'warning': Warning
Expand Down
2 changes: 1 addition & 1 deletion src/icons/close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/icons/plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions src/pages/components/accordion.astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,23 @@ import AstroAccordion from '@components/Accordion/Accordion.astro'
import SvelteAccordion from '@components/Accordion/Accordion.svelte'
import ReactAccordion from '@components/Accordion/Accordion.tsx'
import { getSections } from '@helpers'
import { accordionItems } from '@data'
const alternatingAccordionItems = [
accordionItems[0],
{
...accordionItems[1],
reverse: true
},
accordionItems[2]
]
const sections = getSections({
title: 'accordions',
components: [AstroAccordion, SvelteAccordion, ReactAccordion],
showSubTitle: true
})
---

<Layout>
Expand All @@ -25,4 +41,43 @@ import { accordionItems } from '@data'
<ReactAccordion items={accordionItems} client:load />
</ComponentWrapper>
</div>

{sections.map(section => (
<h1>{section.title}</h1>
<Fragment>
{section.subTitle && <h2 set:html={section.subTitle} />}
</Fragment>
<div class="grid md-2 lg-3">
<ComponentWrapper title="Default">
<section.component items={accordionItems} />
</ComponentWrapper>

<ComponentWrapper title="Plus icon">
<section.component
items={accordionItems}
icon="plus"
/>
</ComponentWrapper>

<ComponentWrapper title="Without icon">
<section.component
items={accordionItems}
icon="none"
/>
</ComponentWrapper>

<ComponentWrapper title="Reverse layout">
<section.component
items={accordionItems}
reverse={true}
/>
</ComponentWrapper>

<ComponentWrapper title="Alternating layout">
<section.component
items={alternatingAccordionItems}
/>
</ComponentWrapper>
</div>
))}
</Layout>

0 comments on commit 4ddf4ff

Please sign in to comment.