diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a5b13b..1f15e96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ O versionamento deste projeto é aderente aos princípios de [Semantic Versionin ## Unreleased +### Added + +- Criação do componente ``. + ### Changed - Melhoria na definição de atributos de acessibilidade no ``. diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 52d267c..4f8237e 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -17,7 +17,7 @@ - Componentes - [Accordion](/componentes/accordion) - - [Accordion Group (em breve)](/componentes/accordion-group) + - [Accordion Group](/componentes/accordion-group) - [Avatar](/componentes/avatar) - [Background](/componentes/background) - [Badge](/componentes/badge) diff --git a/docs/componentes/accordion-group.md b/docs/componentes/accordion-group.md new file mode 100644 index 0000000..12ee5e1 --- /dev/null +++ b/docs/componentes/accordion-group.md @@ -0,0 +1,108 @@ +# Accordion Group + +[component-header:cps-accordion-group] + +```html preview + + + Conteúdo. + + + + Conteúdo. + + + + Conteúdo. + + +``` + +## 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 + + + Conteúdo. + + + + Conteúdo. + + + + Conteúdo. + +``` + +### 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 + + + Conteúdo. + + + + Conteúdo. + + + + Conteúdo. + + +``` + +### 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 + + + + + Conteúdo. + + + + Conteúdo. + + + + Conteúdo. + + +``` + +?> 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] diff --git a/docs/componentes/accordion.md b/docs/componentes/accordion.md index f5d9a10..774ad95 100644 --- a/docs/componentes/accordion.md +++ b/docs/componentes/accordion.md @@ -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 diff --git a/src/all.ts b/src/all.ts index 94434bf..6a28f60 100644 --- a/src/all.ts +++ b/src/all.ts @@ -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'; diff --git a/src/components/accordion-group.ts b/src/components/accordion-group.ts new file mode 100644 index 0000000..e7bfcdc --- /dev/null +++ b/src/components/accordion-group.ts @@ -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 ``. + * + * @csspart base - O elemento principal propriamente dito (um `
`). + * + * @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('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` +
+ +
+ `; + } +} + +export { CpsAccordionGroup }; + +declare global { + interface HTMLElementTagNameMap { + 'cps-accordion-group': CpsAccordionGroup; + } +} diff --git a/src/components/accordion-group/accordion-group.styles.ts b/src/components/accordion-group/accordion-group.styles.ts new file mode 100644 index 0000000..664a13a --- /dev/null +++ b/src/components/accordion-group/accordion-group.styles.ts @@ -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); + } +`;