Skip to content

Commit

Permalink
feat: add accordion
Browse files Browse the repository at this point in the history
  • Loading branch information
adrian-ub committed Aug 28, 2024
1 parent 2d15b65 commit f391f19
Show file tree
Hide file tree
Showing 6 changed files with 390 additions and 58 deletions.
88 changes: 88 additions & 0 deletions apps/www/src/content/docs/docs/components/accordion.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
title: Accordion
description: A vertically stacked set of interactive headings that each reveal a section of content.
links:
doc: https://www.radix-ng.com/?path=/docs/primitives-accordion--docs
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
import { Steps } from "@astrojs/starlight/components";

<ComponentPreview
name="accordion-demo"
class="[&_.preview>[data-orientation=vertical]]:sm:max-w-[70%]"
/>

## Installation

<Tabs>
<TabItem label="CLI">
<Steps>
1. Run the following command:
```bash frame="code"
npx shadcn-ng@latest add accordion
```
2. Update `tailwind.config.js`
Add the following animations to your `tailwind.config.js` file:
```js {5-18}
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
}
```
</Steps>
</TabItem>

<TabItem label="Manual">
<Steps>
1. Install the package using your package manager.
```bash
npm install @ng-icons/core @ng-icons/lucide
```
2. Copy and paste the following code into your project.
<ComponentSource name="accordion" />

3. Update the import paths to match your project setup.
</Steps>

</TabItem>
</Tabs>

## Usage

```ts
import {
RdxAccordionContentDirective,
RdxAccordionHeaderDirective,
RdxAccordionItemDirective,
RdxAccordionRootDirective,
RdxAccordionTriggerDirective
} from '@radix-ng/primitives/accordion';
```

```angular-html
<div ubAccordion class="w-full" orientation="vertical">
<div ubAccordionItem value="item-1">
<ub-accordion-trigger>Is it accessible?</ub-accordion-trigger>
<div ubAccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</div>
</div>
</div>
```
34 changes: 34 additions & 0 deletions apps/www/src/registry/default/example/accordion-demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Component } from '@angular/core';

import { UbAccordionDirective, UbAccordionItemDirective, UbAccordionTriggerDirective, UbAccordionContentDirective } from '@/registry/default/ui/accordion.directive'

@Component({
standalone: true,
selector: 'accordion-demo-default',
imports: [UbAccordionDirective, UbAccordionItemDirective, UbAccordionTriggerDirective, UbAccordionContentDirective],
template: `
<div ubAccordion class="w-full" orientation="vertical">
<div ubAccordionItem value="item-1">
<ub-accordion-trigger>Is it accessible?</ub-accordion-trigger>
<div ubAccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</div>
</div>
<div ubAccordionItem value="item-2">
<ub-accordion-trigger>Is it styled?</ub-accordion-trigger>
<div ubAccordionContent>
Yes. It comes with default styles that matches the other components&apos; aesthetic.
</div>
</div>
<div ubAccordionItem value="item-3">
<ub-accordion-trigger>Is it animated?</ub-accordion-trigger>
<div ubAccordionContent>
Yes. It&apos;s animated by default, but you can disable it if you prefer.
</div>
</div>
</div>
`
})
export class AccordionDemoDefault { }

export default AccordionDemoDefault;
85 changes: 85 additions & 0 deletions apps/www/src/registry/default/ui/accordion.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Component, computed, Directive, input } from '@angular/core';
import type { ClassValue } from 'clsx';

import { NgIconComponent, provideIcons } from "@ng-icons/core";
import { lucideChevronDown } from "@ng-icons/lucide";

import {
RdxAccordionContentDirective,
RdxAccordionHeaderDirective,
RdxAccordionItemDirective,
RdxAccordionRootDirective,
RdxAccordionTriggerDirective
} from '@radix-ng/primitives/accordion';
import { cn } from '@/lib/utils';


@Directive({
standalone: true,
selector: '[ubAccordion]',
hostDirectives: [RdxAccordionRootDirective],
})
export class UbAccordionDirective { }

@Directive({
standalone: true,
selector: '[ubAccordionItem]',
hostDirectives: [
{
directive: RdxAccordionItemDirective,
inputs: ['disabled', 'value']
}
],
host: {
'[class]': 'computedClass()'
}
})
export class UbAccordionItemDirective {
class = input<ClassValue>();
computedClass = computed(() => {
return cn('border-b', this.class());
})
}


@Component({
standalone: true,
selector: '[ubAccordionTrigger], ub-accordion-trigger',
imports: [RdxAccordionHeaderDirective, RdxAccordionTriggerDirective, NgIconComponent],
viewProviders: [provideIcons({ lucideChevronDown })],
template: `
<h3 rdxAccordionHeader class="flex">
<button rdxAccordionTrigger [className]="computedClass()">
<ng-content></ng-content>
<ng-icon name="lucideChevronDown" class="h-4 w-4 shrink-0 transition-transform duration-200"></ng-icon>
</button>
</h3>
`
})
export class UbAccordionTriggerDirective {
class = input<ClassValue>();
computedClass = computed(() => {
return cn('flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>ng-icon]:rotate-180', this.class());
})
}

@Component({
standalone: true,
selector: '[ubAccordionContent], ub-accordion-content',
hostDirectives: [RdxAccordionContentDirective],
host: {
'class':
'overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down'
},
template: `
<div [className]="computedClass()">
<ng-content></ng-content>
</div>
`
})
export class UbAccordionContentDirective {
class = input<ClassValue>();
computedClass = computed(() => {
return cn('pb-4 pt-0', this.class());
})
}
34 changes: 34 additions & 0 deletions apps/www/src/registry/new-york/example/accordion-demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Component } from '@angular/core';

import { UbAccordionDirective, UbAccordionItemDirective, UbAccordionTriggerDirective, UbAccordionContentDirective } from '@/registry/new-york/ui/accordion.directive'

@Component({
standalone: true,
selector: 'accordion-demo-default',
imports: [UbAccordionDirective, UbAccordionItemDirective, UbAccordionTriggerDirective, UbAccordionContentDirective],
template: `
<div ubAccordion class="w-full" orientation="vertical">
<div ubAccordionItem value="item-1">
<ub-accordion-trigger>Is it accessible?</ub-accordion-trigger>
<div ubAccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</div>
</div>
<div ubAccordionItem value="item-2">
<ub-accordion-trigger>Is it styled?</ub-accordion-trigger>
<div ubAccordionContent>
Yes. It comes with default styles that matches the other components&apos; aesthetic.
</div>
</div>
<div ubAccordionItem value="item-3">
<ub-accordion-trigger>Is it animated?</ub-accordion-trigger>
<div ubAccordionContent>
Yes. It&apos;s animated by default, but you can disable it if you prefer.
</div>
</div>
</div>
`
})
export class AccordionDemoDefault { }

export default AccordionDemoDefault;
85 changes: 85 additions & 0 deletions apps/www/src/registry/new-york/ui/accordion.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Component, computed, Directive, input } from '@angular/core';
import type { ClassValue } from 'clsx';

import { NgIconComponent, provideIcons } from "@ng-icons/core";
import { lucideChevronDown } from "@ng-icons/lucide";

import {
RdxAccordionContentDirective,
RdxAccordionHeaderDirective,
RdxAccordionItemDirective,
RdxAccordionRootDirective,
RdxAccordionTriggerDirective
} from '@radix-ng/primitives/accordion';
import { cn } from '@/lib/utils';


@Directive({
standalone: true,
selector: '[ubAccordion]',
hostDirectives: [RdxAccordionRootDirective],
})
export class UbAccordionDirective { }

@Directive({
standalone: true,
selector: '[ubAccordionItem]',
hostDirectives: [
{
directive: RdxAccordionItemDirective,
inputs: ['disabled', 'value']
}
],
host: {
'[class]': 'computedClass()'
}
})
export class UbAccordionItemDirective {
class = input<ClassValue>();
computedClass = computed(() => {
return cn('border-b', this.class());
})
}


@Component({
standalone: true,
selector: '[ubAccordionTrigger], ub-accordion-trigger',
imports: [RdxAccordionHeaderDirective, RdxAccordionTriggerDirective, NgIconComponent],
viewProviders: [provideIcons({ lucideChevronDown })],
template: `
<h3 rdxAccordionHeader class="flex">
<button rdxAccordionTrigger [className]="computedClass()">
<ng-content></ng-content>
<ng-icon name="lucideChevronDown" class="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200"></ng-icon>
</button>
</h3>
`
})
export class UbAccordionTriggerDirective {
class = input<ClassValue>();
computedClass = computed(() => {
return cn('flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>ng-icon]:rotate-180', this.class());
})
}

@Component({
standalone: true,
selector: '[ubAccordionContent], ub-accordion-content',
hostDirectives: [RdxAccordionContentDirective],
host: {
'class':
'overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down'
},
template: `
<div [className]="computedClass()">
<ng-content></ng-content>
</div>
`
})
export class UbAccordionContentDirective {
class = input<ClassValue>();
computedClass = computed(() => {
return cn('pb-4 pt-0', this.class());
})
}
Loading

0 comments on commit f391f19

Please sign in to comment.