Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
feat: implement accordion component (#204)
Browse files Browse the repository at this point in the history
  • Loading branch information
pedronauck authored May 24, 2022
1 parent 14fa486 commit efa2631
Show file tree
Hide file tree
Showing 12 changed files with 311 additions and 360 deletions.
4 changes: 2 additions & 2 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
"@fuel-ts/contract": "0.0.0-master-6c8ce489",
"@fuel-ts/providers": "0.0.0-master-6c8ce489",
"@headlessui/react": "^1.6.2",
"@radix-ui/react-dialog": "^0.1.7",
"@radix-ui/react-tooltip": "^0.1.7",
"@radix-ui/react-accordion": "^0.1.6",
"@react-aria/button": "^3.4.4",
"@react-aria/dialog": "^3.1.9",
"@react-aria/focus": "^3.5.5",
Expand Down Expand Up @@ -72,6 +71,7 @@
"autoprefixer": "^10.4.2",
"eslint": "^8.4.1",
"postcss": "^8.4.7",
"postcss-import": "^14.1.0",
"tailwindcss": "^3.0.23",
"typechain": "^8.0.0",
"typechain-target-fuels": "0.0.0-master-6c8ce489",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/postcss.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module.exports = {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {},
},
Expand Down
102 changes: 102 additions & 0 deletions packages/app/src/components/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import * as AC from "@radix-ui/react-accordion";
import cx from "classnames";
import type { FC } from "react";
import { forwardRef } from "react";
import { BiChevronDown } from "react-icons/bi";

type BaseAccordionProps =
| (AC.AccordionSingleProps & React.RefAttributes<HTMLDivElement>)
| (AC.AccordionMultipleProps & React.RefAttributes<HTMLDivElement>);

export type AccordionProps = BaseAccordionProps & {
className?: string;
};

type AccordionComponent = FC<AccordionProps> & {
Item: typeof AccordionItem;
Trigger: typeof AccordionTrigger;
Content: typeof AccordionContent;
};

/**
* Component implemented based on from Radix's Accordion component.
* You can check about props on their website.
* @see https://www.radix-ui.com/docs/primitives/components/accordion
* @example
* ```jsx
* <Accordion type="single" defaultValue="item-1" collapsible>
* <Accordion.Item value="item-1">
* <Accordion.Trigger>Hello world</Accordion.Trigger>
* <Accordion.Content>
* Yes. It&apos;s unstyled by default, giving you
* <br /> freedom over the look and feel.
* </Accordion.Content>
* </Accordion.Item>
* <Accordion.Item value="item-2">
* <Accordion.Trigger>Is it unstyled?</Accordion.Trigger>
* <Accordion.Content>
* Yes. It&apos;s unstyled by default, giving you
* <br /> freedom over the look and feel.
* </Accordion.Content>
* </Accordion.Item>
* </Accordion>
* ```jsx
*/
export const Accordion: AccordionComponent = ({ className, ...props }) => (
<AC.Root {...props} className={cx("accordion--root", className)} />
);

export type AccordionItemProps = AC.AccordionItemProps & {
className?: string;
};

export const AccordionItem = forwardRef<HTMLDivElement, AccordionItemProps>(
({ className, ...props }, ref) => (
<AC.AccordionItem
{...props}
ref={ref}
className={cx("accordion--item", className)}
/>
)
);

export type AccordionTriggerProps = AC.AccordionTriggerProps & {
className?: string;
};

export const AccordionTrigger = forwardRef<
HTMLButtonElement,
AccordionTriggerProps
>(({ children, className, ...props }, ref) => (
<AC.AccordionHeader className="accordion--header">
<AC.AccordionTrigger
{...props}
ref={ref}
className={cx("accordion--trigger", className)}
>
{children}
<BiChevronDown aria-hidden className="accordion--icon" />
</AC.AccordionTrigger>
</AC.AccordionHeader>
));

export type AccordionContentProps = AC.AccordionContentProps & {
className?: string;
};

export const AccordionContent = forwardRef<
HTMLDivElement,
AccordionContentProps
>(({ children, className, ...props }, ref) => (
<AC.AccordionContent
{...props}
ref={ref}
className={cx("accordion--content", className)}
>
<div>{children}</div>
</AC.AccordionContent>
));

Accordion.Item = AccordionItem;
Accordion.Trigger = AccordionTrigger;
Accordion.Content = AccordionContent;
2 changes: 1 addition & 1 deletion packages/app/src/components/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const Dialog: DialogComponent = ({ state, ...props }) => {
ref
);

usePreventScroll();
usePreventScroll({ isDisabled: !state.isOpen });
const { modalProps } = useModal();
const { dialogProps, titleProps } = useReactAriaDialog(props, ref);
const ctxValue = {
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "@fontsource/fira-code";
import "@fontsource/inter/variable.css";
import "./index.css";
import "./styles/index.css";

import { createRoot } from "react-dom/client";

Expand Down
76 changes: 1 addition & 75 deletions packages/app/src/index.css → packages/app/src/styles/base.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

79 changes: 79 additions & 0 deletions packages/app/src/styles/components/accordion.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
@layer components {
@keyframes slide-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}

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

.accordion--root {
@apply max-w-[100%] rounded-lg;
}

.accordion--item {
@apply overflow-hidden mt-[1px];

&:first-child {
@apply mt-0 rounded-tl-lg rounded-tr-lg;
}
&:last-child {
@apply rounded-bl-lg rounded-br-lg;
}
&:focus-within {
@apply relative z-10;
}

& ~ & {
@apply mt-3;
}
}

.accordion--header {
all: unset;
@apply px-1 flex border-b-2 border-b-gray-700;
}

.accordion--trigger {
all: unset;
@apply flex-1 flex items-center justify-between;
@apply text-gray-200;

&:hover {
@apply cursor-pointer;
}
}

.accordion--icon {
transition: transform 300ms cubic-bezier(0.87, 0, 0.13, 1);

[data-state="open"] & {
transform: rotate(180deg);
}
}

.accordion--content {
@apply overflow-hidden text-gray-400 break-words w-full;

&[data-state="open"] {
animation: slide-down 300ms cubic-bezier(0.87, 0, 0.13, 1) forwards;
}
&[data-state="close"] {
animation: slide-up 300ms cubic-bezier(0.87, 0, 0.13, 1) forwards;
}

& > div {
@apply px-1 mt-3;
}
}
}
39 changes: 39 additions & 0 deletions packages/app/src/styles/components/button.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@layer components {
.button {
@apply appearance-none transition-main inline-flex items-center rounded-md gap-2;
@apply border focus-ring btn-active cursor-pointer;
@apply disabled:cursor-default disabled:opacity-50;
}
.button--sm {
@apply text-sm px-2 h-8;
}
.button--md {
@apply text-base px-4 py-2;
}
.button--lg {
@apply text-lg px-4 py-2;
}
.button--base {
@apply text-gray-400 hover:text-primary-500 border-gray-700;
@apply focus:border-primary-500 active:border-gray-700;
@apply disabled:border-transparent disabled:text-gray-400;
}
.button--ghost {
@apply text-gray-400 border-transparent hover:bg-white/5;
@apply focus:border-primary-500 active:border-gray-700;
@apply disabled:border-transparent disabled:bg-white/0;
}
.button--primary {
@apply bg-primary-500 hover:bg-primary-600;
@apply text-primary-100 font-semibold border-transparent;
@apply focus:ring-primary-300 focus:border-primary-300;
@apply active:border-transparent disabled:bg-primary-500;
}
.btn-active {
@apply active:scale-[0.98] disabled:scale-100;
}
*[aria-pressed="true"],
*[data-pressed="true"] {
@apply scale-[0.9] disabled:scale-100;
}
}
11 changes: 11 additions & 0 deletions packages/app/src/styles/components/coin-selector.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@layer components {
.coin-selector {
@apply h-10 px-2 rounded-xl gap-1 bg-gray-800;
}
.coin-selector:not([aria-disabled="true"]) {
@apply hover:text-gray-300 hover:border-gray-600;
}
.coin-selector[aria-disabled="true"] {
@apply opacity-100;
}
}
10 changes: 10 additions & 0 deletions packages/app/src/styles/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import "tailwindcss/base";
@import "./base.css";

@import "tailwindcss/components";
@import "./components/accordion.css";
@import "./components/button.css";
@import "./components/coin-selector.css";

@import "tailwindcss/utilities";
@import "./utilities.css";
16 changes: 16 additions & 0 deletions packages/app/src/styles/utilities.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@layer utilities {
.focus-ring:not([aria-disabled="true"]) {
@apply focus:outline-none focus:ring-inset focus:ring-1;
@apply focus:ring-primary-500 active:ring-0 disabled:ring-0;
}
.transition-main {
@apply transition ease-in duration-100;
}
.link {
@apply text-primary-400 no-underline hover:underline rounded-lg;
@apply focus:outline-none focus:underline;
}
.inner-shadow {
box-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.2);
}
}
Loading

0 comments on commit efa2631

Please sign in to comment.