Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[accordion] New Accordion components and hooks #577

Merged
merged 54 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
a64950b
Provide Collapsible content width css var
mj12albert Sep 3, 2024
ab49d59
New Accordion hooks and components
mj12albert Aug 22, 2024
12353ff
Add docs and demos
mj12albert Sep 4, 2024
adb4ce2
Misc fixes
mj12albert Sep 4, 2024
357b1f5
Refactor useCollapsibleTrigger with useButton
mj12albert Sep 4, 2024
a211d7b
Fix review comments
mj12albert Sep 11, 2024
895872c
Rename subcomponents
mj12albert Sep 11, 2024
9605d9f
Support focus loop
mj12albert Sep 11, 2024
bfc09a1
Add tests
mj12albert Sep 16, 2024
3c687fa
Rename data-state for Accordion and Collapsible
mj12albert Sep 17, 2024
28978bb
Use a boolean prop to apply hidden-until-found
mj12albert Sep 17, 2024
c16a64a
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Sep 19, 2024
80b04e0
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Sep 20, 2024
0301088
PR review fixes
mj12albert Sep 23, 2024
d00847d
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 9, 2024
b37d00e
Rename onOpenChange to onValueChange and misc fixes
mj12albert Oct 9, 2024
b7c09a0
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 9, 2024
d8d2b68
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 13, 2024
85a3e12
Update experiments
mj12albert Oct 13, 2024
a26bf03
[core] Increase the minimum Node.js version support to 14.0.0 (#658)
oliviertassinari Oct 10, 2024
95e7a1c
Bump Public packages' dependencies (major) (#725)
renovate[bot] Oct 11, 2024
2572aa2
Bump Public packages' dependencies (#724)
renovate[bot] Oct 11, 2024
c28dcf1
[examples] Avoid git diff when playing with examples
oliviertassinari Oct 13, 2024
ef3c012
[Checkbox][Switch] Split data-state into data-checked, data-unchecked…
atomiks Oct 14, 2024
094b558
[core] Improve DX of importing Base UI components (#700)
michaldudak Oct 14, 2024
19cfa44
Update exports and types
mj12albert Oct 14, 2024
24be486
Update demos
mj12albert Oct 14, 2024
24a9abc
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 15, 2024
c76e668
Merge collapsible internal height and width states
mj12albert Oct 15, 2024
f87d7f0
Finalize demo margin padding
mj12albert Oct 16, 2024
283ec66
Refactor collapsible content internals
mj12albert Oct 15, 2024
0d2b45f
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 16, 2024
ddc2547
Improve getting computed styles performance
mj12albert Oct 16, 2024
0b5458f
Rename Collapsible.Content to Panel
mj12albert Oct 16, 2024
7ad7ce1
Update style hooks
mj12albert Oct 16, 2024
710c2cd
Improve tests
mj12albert Oct 16, 2024
788facc
Merge branch 'master' into feat/accordion
mj12albert Oct 16, 2024
5a7803e
Update docs
mj12albert Oct 17, 2024
707249d
Fixes
mj12albert Oct 17, 2024
743c07e
Rename
mj12albert Oct 17, 2024
49598c5
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 17, 2024
ca7e9b4
Fix contexts
mj12albert Oct 17, 2024
8e0df16
Update experiments
mj12albert Oct 17, 2024
03e0ec4
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 21, 2024
7dce2b3
prettier
mj12albert Oct 21, 2024
0321519
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 22, 2024
fc14621
Fix review comments
mj12albert Oct 22, 2024
e1134df
Universally update style hooks from data-attr='true' to data-attr
mj12albert Oct 22, 2024
e7de337
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 24, 2024
7b9bf09
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 28, 2024
91c0cbb
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 29, 2024
217ead7
Merge remote-tracking branch 'upstream/master' into feat/accordion
mj12albert Oct 29, 2024
ec8d70b
Fix conflict
mj12albert Oct 29, 2024
faf9231
Fix jsdom tests
mj12albert Oct 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 190 additions & 0 deletions docs/app/experiments/accordion-animations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
'use client';
import * as React from 'react';
import * as Accordion from '@base_ui/react/Accordion';
import { ExpandMoreIcon } from './accordion';

const DURATION = '300ms';

export default function App() {
return (
<div className="AnimatedAccordions">
<h3>CSS @keyframe animations + `hidden=&quot;until-found&quot;`</h3>
<Accordion.Root
className="MyAccordion-root"
aria-label="Uncontrolled Material UI Accordion"
hiddenUntilFound
>
{[0, 1, 2].map((index) => (
<Accordion.Item className="MyAccordion-item" key={index}>
<Accordion.Header className="MyAccordion-header">
<Accordion.Trigger className="MyAccordion-trigger">
<span className="trigger-text">Trigger {index + 1}</span>
<ExpandMoreIcon />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel className="MyAccordion-panel cssanimation">
<p>
This is the contents of Accordion.Panel {index + 1}
<br />
It uses `hidden=&quot;until-found&quot;` and can be opened by the browser&apos;s
in-page search
</p>
</Accordion.Panel>
</Accordion.Item>
))}
</Accordion.Root>

<h3>CSS transitions</h3>
<Accordion.Root className="MyAccordion-root" aria-label="Uncontrolled Material UI Accordion">
{[0, 1, 2].map((index) => (
<Accordion.Item className="MyAccordion-item" key={index}>
<Accordion.Header className="MyAccordion-header">
<Accordion.Trigger className="MyAccordion-trigger">
<span className="trigger-text">Trigger {index + 1}</span>
<ExpandMoreIcon />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel className="MyAccordion-panel csstransition">
<p>This is the contents of Accordion.Panel {index + 1}</p>
</Accordion.Panel>
</Accordion.Item>
))}
</Accordion.Root>
<MaterialStyles />
</div>
);
}

function MaterialStyles() {
return (
<style suppressHydrationWarning>
{`
.AnimatedAccordions {
width: 40rem;
margin: 1rem;
display: flex;
flex-direction: column;
gap: 2rem;
}

.MyAccordion-root {
--Paper-shadow:
0px 2px 1px -1px rgba(0, 0, 0, 0.2),
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
0px 1px 3px 0px rgba(0, 0, 0, 0.12);

font-family: system-ui, sans-serif;
box-shadow: var(--Paper-shadow);
background-color: rgba(0,0,0,0.12);
border-radius: .3rem;
}

.MyAccordion-item {
position: relative;
background-color: #fff;
color: rgba(0, 0, 0, .87);
}

.MyAccordion-item:not(:first-of-type) {
margin-top: 1px;
}

.MyAccordion-item:first-of-type {
border-top-left-radius: .25rem;
border-top-right-radius: .25rem;
}

.MyAccordion-item:last-of-type {
border-bottom-left-radius: .25rem;
border-bottom-right-radius: .25rem;
}

.MyAccordion-header {
margin: 0;
}

.MyAccordion-trigger {
appearance: none;
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
padding: 0 1rem;
position: relative;
width: 100%;
display: flex;
flex-flow: row nowrap;
align-items: center;
}

.MyAccordion-trigger:focus-visible {
outline: 0;
background-color: rgba(0,0,0,0.12);
}

.MyAccordion-trigger .trigger-text {
font-size: 1rem;
line-height: 1.5;
margin: 12px auto 12px 0;
}

.MyAccordion-trigger svg {
transition: transform 300ms;
}

.MyAccordion-trigger[data-collapsible="open"] svg {
transform: rotate(180deg);
}

.MyAccordion-panel {
overflow: hidden;
}

.MyAccordion-panel p {
margin: 0;
padding: 1rem;
}

.MyAccordion-panel.cssanimation[data-collapsible="open"] {
animation: slideDown ${DURATION} ease-out;
}

.MyAccordion-panel.cssanimation[data-collapsible="closed"] {
animation: slideUp ${DURATION} ease-out;
}

@keyframes slideDown {
from {
height: 0;
}
to {
height: var(--accordion-content-height);
}
}

@keyframes slideUp {
from {
height: var(--accordion-content-height);
}
to {
height: 0;
}
}

.MyAccordion-panel.csstransition[data-collapsible="open"] {
height: var(--accordion-content-height);
transition: height ${DURATION} ease-out;
}

.MyAccordion-panel.csstransition[data-collapsible="closed"] {
height: 0;
transition: height ${DURATION} ease-in;
}

.MyAccordion-panel.csstransition[data-entering] {
height: 0;
}
`}
</style>
);
}
180 changes: 180 additions & 0 deletions docs/app/experiments/accordion-horizontal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
'use client';
import * as React from 'react';
import * as Accordion from '@base_ui/react/Accordion';

function classNames(...classes: Array<string | undefined | null | false>) {
return classes.filter(Boolean).join(' ');
}

export default function App() {
const [val, setVal] = React.useState(['one']);
return (
<div className="HorizontalAccordionDemo">
<h2>Horizontal LTR</h2>
<Accordion.Root
className="MyHorizontalAccordion-root"
aria-label="Uncontrolled Horizontal Accordion"
openMultiple={false}
orientation="horizontal"
>
{['one', 'two', 'three'].map((value, index) => (
<Accordion.Item className="MyHorizontalAccordion-section" key={value}>
<Accordion.Header className="MyHorizontalAccordion-heading">
<Accordion.Trigger className={classNames('MyHorizontalAccordion-trigger', value)}>
<span className="trigger-text">{index + 1}</span>
<span className="trigger-label">{value}</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel className="MyHorizontalAccordion-panel">
This is the contents of Accordion.Panel {index + 1}
</Accordion.Panel>
</Accordion.Item>
))}
</Accordion.Root>

<span>
<h2>Horizontal RTL</h2>
<p>one section must remain open</p>
</span>
<Accordion.Root
className="MyHorizontalAccordion-root"
aria-label="Controlled Horizontal RTL Accordion"
openMultiple={false}
orientation="horizontal"
direction="rtl"
value={val}
onOpenChange={(newValue: Accordion.Root.Props['Value']) => {
if (newValue.length > 0) {
setVal(newValue);
}
}}
>
{['one', 'two', 'three'].map((value, index) => (
<Accordion.Item className="MyHorizontalAccordion-section" key={value} value={value}>
<Accordion.Header className="MyHorizontalAccordion-heading">
<Accordion.Trigger className={classNames('MyHorizontalAccordion-trigger', value)}>
<span className="trigger-text">{index + 1}</span>
<span className="trigger-label">{value}</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel className="MyHorizontalAccordion-panel">
This is the contents of Accordion.Panel {index + 1}
</Accordion.Panel>
</Accordion.Item>
))}
</Accordion.Root>
<HorizontalStyles />
</div>
);
}

function HorizontalStyles() {
return (
<style suppressHydrationWarning>
{`
.HorizontalAccordionDemo {
margin: 1rem;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 2rem;
}

.MyHorizontalAccordion-root {
--Paper-shadow:
0px 2px 1px -1px rgba(0, 0, 0, 0.2),
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
0px 1px 3px 0px rgba(0, 0, 0, 0.12);

font-family: system-ui, sans-serif;
box-shadow: var(--Paper-shadow);
background-color: rgba(0,0,0,0.12);
border-radius: .3rem;
height: 40rem;
display: inline-flex;
}

.MyHorizontalAccordion-section {
position: relative;
background-color: #fff;
color: rgba(0, 0, 0, .87);
display: flex;
}

.MyHorizontalAccordion-section:not(:first-of-type) {
margin-left: 1px;
}

.MyHorizontalAccordion-section:first-of-type {
border-top-left-radius: .25rem;
border-top-right-radius: .25rem;
}

.MyHorizontalAccordion-section:last-of-type {
border-bottom-left-radius: .25rem;
border-bottom-right-radius: .25rem;
}

.MyHorizontalAccordion-heading {
margin: 0;
width: 4rem;
}

.MyHorizontalAccordion-trigger {
appearance: none;
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
padding: 1rem;
position: relative;
height: 100%;
width: 100%;
display: flex;
flex-flow: column nowrap;
align-items: center;
}

.MyHorizontalAccordion-trigger.one {
background-color: #ddd;
}

.MyHorizontalAccordion-trigger.two {
background-color: #bbb;
}

.MyHorizontalAccordion-trigger.three {
background-color: #999;
}

.MyHorizontalAccordion-trigger:focus-visible {
outline: 0;
background-color: rgba(0,0,0,0.88);
color: #eee;
}

.MyHorizontalAccordion-trigger .trigger-text {
font-size: 1rem;
line-height: 1.5;
margin-bottom: auto;
}

.MyHorizontalAccordion-trigger .trigger-label {
font-size: 1rem;
line-height: 1.5;
margin-bottom: 1rem;
transform: rotate(-90deg);
}

.MyHorizontalAccordion-trigger[data-accordion="open"] svg {
transform: rotate(180deg);
}

.MyHorizontalAccordion-panel {
padding: 1rem;
width: 32rem;
}
`}
</style>
);
}
Loading