Skip to content

Commit a6ce9a7

Browse files
docs: improved accordion docs changed
1 parent 45fc680 commit a6ce9a7

File tree

10 files changed

+188
-66
lines changed

10 files changed

+188
-66
lines changed

apps/website/adapters/cloudflare-pages/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { cloudflarePagesAdapter } from '@builder.io/qwik-city/adapters/cloudflar
22
import { extendConfig } from '@builder.io/qwik-city/vite';
33
import baseConfig from '../../vite.config';
44

5+
// @ts-ignore
56
export default extendConfig(baseConfig, () => {
67
return {
78
build: {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { component$, useStyles$ } from '@builder.io/qwik';
2+
import { Accordion } from '@qwik-ui/headless';
3+
4+
export default component$(() => {
5+
useStyles$(styles);
6+
const items = [1, 2, 3, 4];
7+
8+
return (
9+
<Accordion.Root disabled={true}>
10+
{items.map((item) => (
11+
<Accordion.Item class="collapsible" key={item}>
12+
<Accordion.Header>
13+
<Accordion.Trigger class="collapsible-trigger">
14+
<span>Trigger {item}</span>
15+
<LuChevronDown />
16+
</Accordion.Trigger>
17+
</Accordion.Header>
18+
<Accordion.Content class="collapsible-content collapsible-content-outline">
19+
Inside Content {item}
20+
</Accordion.Content>
21+
</Accordion.Item>
22+
))}
23+
</Accordion.Root>
24+
);
25+
});
26+
27+
// interal
28+
import styles from '../snippets/accordion.css?inline';
29+
import { LuChevronDown } from '@qwikest/icons/lucide';

apps/website/src/routes/docs/headless/accordion/examples/dynamic.tsx

Lines changed: 44 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -26,65 +26,52 @@ export default component$(({ itemsLength = 3 }: DynamicAccordionProps) => {
2626

2727
return (
2828
<>
29-
<div class="flex w-full flex-col items-center">
30-
<div class="flex gap-4">
31-
<label class="mb-4 flex flex-col-reverse items-center text-center">
32-
<input
33-
class="max-w-[50px] rounded-base bg-accent px-2"
34-
type="text"
35-
bind:value={itemIndexToAdd}
36-
/>
37-
<span>Index to Add</span>
38-
</label>
29+
<div class="dynamic-input">
30+
<label class="add">
31+
<input bind:value={itemIndexToAdd} />
32+
<span>Index to Add</span>
33+
</label>
3934

40-
<label class="mb-4 flex flex-col-reverse items-center text-center">
41-
<input
42-
class="max-w-[50px] rounded-base bg-accent px-2"
43-
type="text"
44-
bind:value={itemIndexToDelete}
45-
/>
46-
<span>Index to Delete</span>
47-
</label>
48-
</div>
35+
<label class="delete">
36+
<input bind:value={itemIndexToDelete} />
37+
<span>Index to Delete</span>
38+
</label>
39+
</div>
40+
41+
<Accordion.Root>
42+
{itemStore.map(({ label, id }, index) => {
43+
return (
44+
<Accordion.Item id={`${id}`} key={id} class="collapsible">
45+
<Accordion.Header>
46+
<Accordion.Trigger class="collapsible-trigger">{label}</Accordion.Trigger>
47+
</Accordion.Header>
48+
<Accordion.Content class="collapsible-content collapsible-content-outline">
49+
index: {index}
50+
</Accordion.Content>
51+
</Accordion.Item>
52+
);
53+
})}
54+
</Accordion.Root>
4955

50-
<Accordion.Root>
51-
{itemStore.map(({ label, id }, index) => {
52-
return (
53-
<Accordion.Item id={`${id}`} key={id} class="collapsible">
54-
<Accordion.Header>
55-
<Accordion.Trigger class="collapsible-trigger">
56-
{label}
57-
</Accordion.Trigger>
58-
</Accordion.Header>
59-
<Accordion.Content class="collapsible-content">
60-
index: {index}
61-
</Accordion.Content>
62-
</Accordion.Item>
63-
);
64-
})}
65-
</Accordion.Root>
66-
<div class="flex gap-2 md:gap-4">
67-
<button
68-
style={{ color: 'green', marginTop: '1rem' }}
69-
onClick$={() => {
70-
if (itemStore.length < 6) {
71-
itemStore.splice(parseInt(itemIndexToAdd.value), 0, newItem);
72-
}
73-
}}
74-
>
75-
<strong>Add Item</strong>
76-
</button>
77-
<button
78-
style={{ color: 'red', marginTop: '1rem' }}
79-
onClick$={() => {
80-
if (itemStore.length > 2) {
81-
itemStore.splice(parseInt(itemIndexToDelete.value), 1);
82-
}
83-
}}
84-
>
85-
<strong>Remove Item</strong>
86-
</button>
87-
</div>
56+
<div class="dynamic-buttons">
57+
<button
58+
onClick$={() => {
59+
if (itemStore.length < 6) {
60+
itemStore.splice(parseInt(itemIndexToAdd.value), 0, newItem);
61+
}
62+
}}
63+
>
64+
<strong>Add Item</strong>
65+
</button>
66+
<button
67+
onClick$={() => {
68+
if (itemStore.length > 2) {
69+
itemStore.splice(parseInt(itemIndexToDelete.value), 1);
70+
}
71+
}}
72+
>
73+
<strong>Remove Item</strong>
74+
</button>
8875
</div>
8976
</>
9077
);

apps/website/src/routes/docs/headless/accordion/index.mdx

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,38 +75,68 @@ Qwik UI includes a headless Accordion component that uses ARIA and JavaScript to
7575
7676
## Component State
7777

78-
## Initial value
78+
### Initial value
79+
80+
To set a default or initial value on page load, use the `value` prop on the `<Accordion.Root />` component.
7981

8082
<Showcase name="initial" />
8183

82-
## Reactive value
84+
The `value` prop on the `<Accordion.Root>` was set to `item-2`, which is the value of the second item. As a result, the second item is selected by default.
85+
86+
### Reactive value
87+
88+
Pass reactive state by using the `bind:value` prop on the `<Accordion.Root /> `component.
8389

8490
<Showcase name="reactive" />
8591

86-
## Programmatic changes
92+
### Programmatic changes
93+
94+
You can also change the current expanded item values programmatically by updating the signal's value.
8795

8896
<Showcase name="programmatic" />
8997

90-
## Handling selection changes
98+
### Handling selection changes
99+
100+
Listen to when a new item is selected by passing a callback function to the `onChange$` prop.
91101

92102
<Showcase name="on-change" />
93103

94-
## Disabled items
104+
### Disabled items
105+
106+
Items can be disabled by setting the `disabled` prop to true on the `<Accordion.Item />` component.
95107

96108
<Showcase name="disabled" />
97109

110+
> Disabled items are not selectable or focusable. They are also skipped when using the arrow keys to navigate through the items.
111+
112+
### Disabled Component
113+
114+
The component itself can also be disabled by setting the `disabled` prop to true on the `<Accordion.Root />` component.
115+
116+
<Showcase name="disabled-root" />
117+
98118
## Animation
99119

120+
Animations can also be added using the `--qwikui-collapsible-content-height` variable.
121+
100122
<Showcase name="animation" />
101123

124+
<CodeSnippet name="animation.css" />
125+
102126
## CSR
103127

128+
The Accordion automatically renders based on its environment. This means that it works for both server-side and client-side rendering.
129+
104130
<Showcase name="csr" />
105131

106132
## Dynamic
107133

134+
You have custom control over how to render items in the Accordion, allowing for dynamic rendering of items.
135+
108136
<Showcase name="dynamic" />
109137

138+
> This also updates the selected items, disabled items, and focus order automatically for you.
139+
110140
## Example CSS
111141

112142
<CodeSnippet name="accordion.css" />

apps/website/src/routes/docs/headless/accordion/snippets/accordion.css

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,39 @@
9090
.collapsible-animation[data-closed] {
9191
animation: 350ms cubic-bezier(0.87, 0, 0.13, 1) 0s 1 normal forwards collapsible-closed;
9292
}
93+
94+
.dynamic-input {
95+
display: flex;
96+
gap: 1rem;
97+
justify-content: center;
98+
}
99+
100+
.dynamic-input label {
101+
display: flex;
102+
flex-direction: column;
103+
text-align: center;
104+
margin-block: 0.5rem;
105+
align-items: center;
106+
}
107+
108+
.dynamic-input input {
109+
margin-bottom: 0.5rem;
110+
width: 5rem;
111+
background: hsl(var(--accent));
112+
border: 2px dotted hsl(var(--foreground));
113+
padding-left: 0.5rem;
114+
}
115+
116+
.dynamic-input .add input {
117+
background: hsla(var(--primary) / 0.2);
118+
}
119+
120+
.dynamic-input .delete input {
121+
background: hsla(var(--secondary) / 0.2);
122+
}
123+
124+
.dynamic-buttons {
125+
display: flex;
126+
gap: 1rem;
127+
margin-block: 0.5rem;
128+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
@keyframes collapsible-open {
2+
0% {
3+
height: 0;
4+
}
5+
100% {
6+
height: var(--qwikui-collapsible-content-height);
7+
}
8+
}
9+
10+
@keyframes collapsible-closed {
11+
0% {
12+
height: var(--qwikui-collapsible-content-height);
13+
}
14+
100% {
15+
height: 0;
16+
}
17+
}
18+
19+
.collapsible-animation[data-open] {
20+
animation: 550ms cubic-bezier(0.87, 0, 0.13, 1) 0s 1 normal forwards collapsible-open;
21+
}
22+
23+
.collapsible-animation[data-closed] {
24+
animation: 350ms cubic-bezier(0.87, 0, 0.13, 1) 0s 1 normal forwards collapsible-closed;
25+
}

packages/kit-headless/src/components/accordion/accordion-context.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type AccordionContext = {
1010
onChange$: QRL<(value: string) => void> | undefined;
1111
itemsMapSig?: Signal<Map<number, boolean>> | undefined;
1212
triggerRefsArray: Signal<Array<Signal>>;
13+
disabled?: boolean;
1314
};
1415

1516
export const accordionItemContextId =

packages/kit-headless/src/components/accordion/accordion-inline.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ export type AccordionRootProps = PropsOf<'div'> & {
2020

2121
/** A map of the item indexes and their disabled state. */
2222
itemsMap?: Map<number, boolean>;
23+
24+
/** If true, the accordion is disabled. */
25+
disabled?: boolean;
2326
};
2427

2528
export const HAccordionRoot: Component<AccordionRootProps> = (

packages/kit-headless/src/components/accordion/accordion-item.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,13 @@ export const HAccordionItem = component$(
9898
useContextProvider(accordionItemContextId, itemContext);
9999

100100
return (
101-
<HCollapsible triggerRef={triggerRef} bind:open={isOpenSig} id={itemId} {...props}>
101+
<HCollapsible
102+
triggerRef={triggerRef}
103+
bind:open={isOpenSig}
104+
id={itemId}
105+
disabled={context.disabled || props.disabled}
106+
{...props}
107+
>
102108
<Slot />
103109
</HCollapsible>
104110
);

packages/kit-headless/src/components/accordion/accordion-root.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
useComputed$,
66
useContextProvider,
77
useSignal,
8+
useTask$,
89
} from '@builder.io/qwik';
910
import { AccordionRootProps } from './accordion-inline';
1011
import { accordionContextId } from './accordion-context';
@@ -15,12 +16,10 @@ export const HAccordionRootImpl = component$((props: AccordionRootProps) => {
1516
'bind:value': givenValueSig,
1617
initialIndex,
1718
onChange$,
18-
itemsMap,
19+
disabled,
1920
...rest
2021
} = props;
2122

22-
itemsMap;
23-
2423
const selectedIndexSig = useSignal<number>(initialIndex ?? -1);
2524
const triggerRefsArray = useSignal<Array<Signal>>([]);
2625

@@ -36,8 +35,13 @@ export const HAccordionRootImpl = component$((props: AccordionRootProps) => {
3635
onChange$,
3736
itemsMapSig,
3837
triggerRefsArray,
38+
disabled,
3939
};
4040

41+
useTask$(({ track }) => {
42+
context.disabled = track(() => disabled);
43+
});
44+
4145
useContextProvider(accordionContextId, context);
4246

4347
return (

0 commit comments

Comments
 (0)