Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

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;
const isTargetOpen = target?.hasAttribute('open');
contextService.items.forEach((i: HTMLElement) => {
i.removeAttribute('open');
});
isTargetOpen
? target?.removeAttribute('open')
: target?.setAttribute('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