Skip to content

Commit

Permalink
feat: add accordion-group component
Browse files Browse the repository at this point in the history
  • Loading branch information
ErickPetru committed May 20, 2024
1 parent 2d4171d commit 3967001
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ O versionamento deste projeto é aderente aos princípios de [Semantic Versionin

## Unreleased

### Added

- Criação do componente `<cps-accordion-group>`.

### Changed

- Melhoria na definição de atributos de acessibilidade no `<cps-accordion>`.
Expand Down
2 changes: 1 addition & 1 deletion docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
- Componentes

- [Accordion](/componentes/accordion)
- [Accordion Group <small>(em breve)</small>](/componentes/accordion-group)
- [Accordion Group](/componentes/accordion-group)
- [Avatar](/componentes/avatar)
- [Background](/componentes/background)
- [Badge](/componentes/badge)
Expand Down
108 changes: 108 additions & 0 deletions docs/componentes/accordion-group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Accordion Group

[component-header:cps-accordion-group]

```html preview
<cps-accordion-group>
<cps-accordion title="Dados pessoais">
<cps-label>Conteúdo.</cps-label>
</cps-accordion>

<cps-accordion title="Endereço">
<cps-label>Conteúdo.</cps-label>
</cps-accordion>

<cps-accordion title="Informações adicionais">
<cps-label>Conteúdo.</cps-label>
</cps-accordion>
</cps-accordion-group>
```

## Exemplos

### Item aberto inicialmente

Para definir um item aberto inicialmente, use o atributo `open` no próprio `cps-accordion` desejado. O grupo de _accordions_ controla a operação de um item aberto por vez somente durante interações do usuário posteriores à exibição inicial.

```html preview
<cps-accordion-group>
<cps-accordion title="Dados pessoais">
<cps-label>Conteúdo.</cps-label>
</cps-accordion>

<cps-accordion title="Endereço" open>
<cps-label>Conteúdo.</cps-label>
</cps-accordion>

<cps-accordion title="Informações adicionais">
<cps-label>Conteúdo.</cps-label>
</cps-accordion>
```

### Múltiplos itens abertos

Use o atributo `multiple` para permitir que múltiplos itens sejam abertos simultaneamente. Neste caso, o grupo de _accordions_ não controla a operação de um item aberto por vez, o que é exatamente o mesmo comportamento de utilizar os _accordions_ de forma independente. Ainda assim, é útil quando se deseja garantir a semântica apropriada do código independentemente do controle dos _accordions_ abertos.

```html preview
<cps-accordion-group multiple>
<cps-accordion title="Dados pessoais" open>
<cps-label>Conteúdo.</cps-label>
</cps-accordion>

<cps-accordion title="Endereço" open>
<cps-label>Conteúdo.</cps-label>
</cps-accordion>

<cps-accordion title="Informações adicionais">
<cps-label>Conteúdo.</cps-label>
</cps-accordion>
</cps-accordion-group>
```

### Distância entre os itens

Use a variável CSS `--gap` para definir a distância entre os itens do grupo de _accordions_. O valor padrão é `--cps-spacing-3`, comumente equivalente a `12px` se você não personalizou o tema padrão.

No exemplo a seguir, o espaçamento entre _accordions_ foi completamente zerado, e então o estilo dos itens foi ajustado para eliminar arredondamento de cantos e bordas duplicadas quando itens se encostam.

```html preview
<style>
.custom-gap {
--gap: 0;
}
.custom-gap cps-accordion::part(header) {
border-radius: 0;
}
.custom-gap cps-accordion:not(:last-of-type) {
margin-bottom: -1px;
}
.custom-gap cps-accordion:not(:last-of-type)::part(content) {
border-bottom-color: transparent;
}
.custom-gap cps-accordion:not([open]):not(:last-of-type)::part(header) {
border-bottom-color: transparent;
}
</style>

<cps-accordion-group class="custom-gap">
<cps-accordion title="Dados pessoais">
<cps-label>Conteúdo.</cps-label>
</cps-accordion>

<cps-accordion title="Endereço">
<cps-label>Conteúdo.</cps-label>
</cps-accordion>

<cps-accordion title="Informações adicionais">
<cps-label>Conteúdo.</cps-label>
</cps-accordion>
</cps-accordion-group>
```

?> Se você está trabalhando em projeto aderente ao CPS Design System, para garantir plena conformidade com as [definições de _accordion_](https://cpsrepositorio.github.io/cps-design-system/documentacao/accordion.htmls), não personalize a variável CSS `--gap` ou o estilo dos elementos internos. Estas opções estão disponíveis para situações que não exigem tal aderência.

[component-metadata:cps-accordion-group]
2 changes: 2 additions & 0 deletions docs/componentes/accordion.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const App = () => (
);
```

?> É possível usar _accordions_ independentes, como exibido nesta página. Entretanto, se quiser _accordions_ mutuamente excludentes em relação à sua expansão/contração, recomendamos o uso do [grupo de _accordions_](/componentes/accordion-group).

## Exemplos

### Abrir e fechar
Expand Down
1 change: 1 addition & 0 deletions src/all.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Components
export { CpsAccordion } from './components/accordion.js';
export { CpsAccordionGroup } from './components/accordion-group.js';
export { CpsAvatar } from './components/avatar.js';
export { CpsBackground } from './components/background.js';
export { CpsBadge } from './components/badge.js';
Expand Down
101 changes: 101 additions & 0 deletions src/components/accordion-group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import type { CSSResultGroup } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { html } from 'lit/static-html.js';
import BaseElement from '../internal/base-element.js';
import styles from './accordion-group/accordion-group.styles.js';
import type CpsAccordion from './accordion.js';

/**
* @summary Grupos de _accordions_ permitem automatizar expansão/contração exclusiva de múltiplos [_accordions_](/componentes/accordion).
* @documentation https://cpsrepositorio.github.io/cps-elements/#/componentes/accordion-group
* @status stable
* @since 0.14
*
* @slot - O conteúdo do grupo, necessariamente um ou mais de `<cps-accordion>`.
*
* @csspart base - O elemento principal propriamente dito (um `<div>`).
*
* @cssproperty --gap - O espaçamento entre _accordions_ dentro do grupo. Padrão: `var(--cps-spacing-3)`.
*/
@customElement('cps-accordion-group')
export default class CpsAccordionGroup extends BaseElement {
static styles: CSSResultGroup = styles;

private activeAccordion?: CpsAccordion;
private accordions: CpsAccordion[] = [];

/** Permite que múltiplos _accordions_ sejam abertos simultaneamente. */
@property({ reflect: true, type: Boolean }) multiple = false;

connectedCallback() {
super.connectedCallback();

this.updateComplete.then(() => {
this.accordions = this.getAllAccordions();
});
}

private getAllAccordions() {
const slot = this.shadowRoot!.querySelector<HTMLSlotElement>('slot')!;

return [...(slot.assignedElements() as CpsAccordion[])].filter(el => {
return el.tagName.toLowerCase() === 'cps-accordion';
});
}

/*
private getActiveAccordion() {
return this.accordions.find(el => el.open);
}
*/

private setActiveAccordion(accordion: CpsAccordion) {
if (accordion !== this.activeAccordion) {
this.activeAccordion = accordion;
this.accordions.forEach(el => (el.open = el === this.activeAccordion));
}
}

private handleClick(event: MouseEvent) {
// Nothing to do if multiple accordions are allowed to be open
if (this.multiple) {
return;
}

const target = event.target as HTMLElement;
const accordion = target.closest('cps-accordion');
const accordionGroup = accordion?.closest('cps-accordion-group');

// Ensure the target accordion is in this accordion group
if (accordionGroup !== this) {
return;
}

if (accordion !== null) {
this.setActiveAccordion(accordion);
}
}

render() {
return html`
<div
class=${classMap({
'accordion-group': true,
})}
part="base"
@click=${this.handleClick}
>
<slot></slot>
</div>
`;
}
}

export { CpsAccordionGroup };

declare global {
interface HTMLElementTagNameMap {
'cps-accordion-group': CpsAccordionGroup;
}
}
18 changes: 18 additions & 0 deletions src/components/accordion-group/accordion-group.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { css } from 'lit';
import componentStyles from '../../styles/component.styles';

export default css`
${componentStyles}
:host {
display: block;
--gap: var(--cps-spacing-3);
}
.accordion-group {
display: flex;
flex-direction: column;
gap: var(--gap);
}
`;

0 comments on commit 3967001

Please sign in to comment.