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

feat: implement Accordion component #204

Merged
merged 1 commit into from
May 24, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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-d5e02003",
"@fuel-ts/providers": "0.0.0-master-d5e02003",
"@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-d5e02003",
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);
}
}