Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions apps/website/src/components/menu/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ type Props = {
export const Menu = component$<Props>(({ onClose$ }) => {
const appState = useContext(APP_STATE);
const menu = [
{
label: 'Accordion',
path: `/docs/${appState.theme.toLowerCase()}/accordion`,
},
{ label: 'Button', path: `/docs/${appState.theme.toLowerCase()}/button` },
{
label: 'ButtonGroup',
Expand Down
56 changes: 56 additions & 0 deletions apps/website/src/routes/docs/daisy/accordion/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { component$, useStylesScoped$ } from '@builder.io/qwik';
import { Accordion, AccordionItem } from '@qwik-ui/theme-daisy';

export default component$(() => {
useStylesScoped$(`
h1 { margin: 2rem 0; padding-top: 1rem; font-weight: bold; border-top: 1px dotted #222}
.container { width: 300px } Accordion {border: 1px solid white}
`);
return (
<>
<div className="container">
<h2>This is the documentation for the Accordion</h2>

<h1>Accordion Example</h1>
<Accordion>
<AccordionItem label="Heading 1">
<div class="p-2">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Accusamus aliquid architecto delectus deleniti dolor
</p>
</div>
</AccordionItem>
<AccordionItem label="Heading 2">
<div className="p-2">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Accusamus aliquid architecto delectus deleniti dolor
</p>
</div>
</AccordionItem>
</Accordion>

<h1>Accordion with Disabled Item Example</h1>
<Accordion>
<AccordionItem label="Heading 1">
<div class="p-2">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Accusamus aliquid architecto delectus deleniti dolor
</p>
</div>
</AccordionItem>
<AccordionItem label="Heading 2 - Disabled" disabled>
<div className="p-2">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Accusamus aliquid architecto delectus deleniti dolor
</p>
</div>
</AccordionItem>
</Accordion>
</div>
</>
);
});
51 changes: 51 additions & 0 deletions apps/website/src/routes/docs/headless/accordion/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { component$, useStylesScoped$ } from '@builder.io/qwik';
import { Accordion, AccordionItem } from '@qwik-ui/headless';

export default component$(() => {
useStylesScoped$(`
h1 { margin: 2rem 0; padding-top: 1rem; font-weight: bold; border-top: 1px dotted #222}
.container { width: 300px }
`);
return (
<div class="container">
<h2>This is the documentation for the Accordion</h2>

<h1>Accordion Example</h1>

<Accordion>
<AccordionItem label="Heading 1">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus
aliquid architecto delectus deleniti dolor
</p>
</AccordionItem>
<AccordionItem label="Heading 2">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus
aliquid architecto delectus deleniti dolor
</p>
</AccordionItem>
</Accordion>

<h1>Accordion with Disabled Item Example</h1>
<Accordion>
<AccordionItem label="Heading 1">
<div>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Accusamus aliquid architecto delectus deleniti dolor
</p>
</div>
</AccordionItem>
<AccordionItem label="Heading 2 - Disabled" disabled>
<div>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Accusamus aliquid architecto delectus deleniti dolor
</p>
</div>
</AccordionItem>
</Accordion>
</div>
);
});
45 changes: 45 additions & 0 deletions packages/daisy/src/components/accordion/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
component$,
HTMLAttributes,
PropFunction,
Slot,
} from '@builder.io/qwik';

import {
Accordion as HeadlessAccordion,
AccordionItem as HeadlessAccordionItem,
} from '@qwik-ui/headless';
import { clsq } from '@qwik-ui/shared';

export type AccordionProps = HTMLAttributes<HTMLElement>;
export interface AccordionItemProps {
label: string;
disabled?: boolean;
class?: string;
style?: string;
onClick$?: PropFunction<() => void>;
}

export const Accordion = component$((props: AccordionProps) => {
const { class: classNames, ...rest } = props;
return (
<HeadlessAccordion
class={clsq('border collapse border collapse-arrow', classNames)}
{...rest}
>
<Slot />
</HeadlessAccordion>
);
});

export const AccordionItem = component$((props: AccordionItemProps) => {
const { class: classNames, ...rest } = props;
return (
<HeadlessAccordionItem
class={clsq('collapse-title border', classNames)}
{...rest}
>
<Slot />
</HeadlessAccordionItem>
);
});
1 change: 1 addition & 0 deletions packages/daisy/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './components/accordion/accordion';
export * from './components/button/button';
export * from './components/progress/progress';
export * from './components/button-group/button-group';
Expand Down
100 changes: 100 additions & 0 deletions packages/headless/src/components/accordion/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {
$,
component$,
createContextId,
HTMLAttributes,
PropFunction,
QRL,
Signal,
Slot,
useBrowserVisibleTask$,
useContext,
useContextProvider,
useSignal,
useStore,
useStylesScoped$,
} from '@builder.io/qwik';

export type AccordionProps = HTMLAttributes<HTMLElement>;

export const accordionContext =
createContextId<AccordionContextService>('accordion');

interface AccordionContextService {
items: HTMLElement[];
setItemsBoxRef$: QRL<(ref: Signal<HTMLElement | undefined>) => void>;
}

interface AccordionItemProps {
label: string;
disabled?: boolean;
class?: string;
style?: string;
onClick$?: PropFunction<() => void>;
}

export const Accordion = component$((props: AccordionProps) => {
const items = useStore([]);
const itemsBoxRef = useSignal<HTMLElement>();
const setItemsBoxRef$ = $((ref: Signal<HTMLElement | undefined>) => {
if (ref) {
itemsBoxRef.value = ref.value;
}
});
const contextService: AccordionContextService = {
items,
setItemsBoxRef$,
};

useContextProvider(accordionContext, contextService);

useBrowserVisibleTask$(() => {
contextService.setItemsBoxRef$(itemsBoxRef);
const items = itemsBoxRef.value?.querySelectorAll<HTMLElement>('div.item');
if (items?.length) {
items.forEach((item) => contextService.items.push(item));
}
});

return (
<div {...props} ref={itemsBoxRef}>
<Slot></Slot>
</div>
);
});

export const AccordionItem = component$((props: AccordionItemProps) => {
useStylesScoped$(`
div.item .content {
display: none;
}
div.item[open] .content {
display: block;
}
`);
const contextService = useContext(accordionContext);
return (
<div class="item">
<button
onClick$={(e) => {
const target = (e.target as HTMLElement).parentElement;
contextService.items.forEach((i: HTMLElement) => {
if (target === i) {
return;
}
i.removeAttribute('open');
});
target?.toggleAttribute('open');
}}
style={props.style}
class={props.class}
disabled={props.disabled}
>
{props.label}
</button>
<div class="content">
<Slot />
</div>
</div>
);
});
1 change: 1 addition & 0 deletions packages/headless/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './components/accordion/accordion';
export * from './components/button/button';
export * from './components/progress/progress';
export * from './components/button-group/button-group';
Expand Down