diff --git a/docs/docs/components/accordion.mdx b/docs/docs/components/accordion.mdx new file mode 100644 index 0000000..79311dd --- /dev/null +++ b/docs/docs/components/accordion.mdx @@ -0,0 +1,238 @@ +# Accordion + +### Quick start + +Here's a quick start guide to get started with the Accordion component + +### Importing Component + +```jsx +import { + Accordion, + AccordionSummary, + AccordionContent, + AccordionGroup, +} from "@hover-design/react"; +``` + +### Code Snippets and Examples + +##### Accordion Default + +The Accordion makes use of `Accordion`, `AccordionSummary`, and `AccordionContent` components to create an accordion. + +It renders a `details` element and `summary` element for the best semantic structure + +import "@hover-design/react/dist/style.css"; + +import { + Accordion, + AccordionSummary, + AccordionContent, + AccordionGroup, + Switch, +} from "@hover-design/react"; + +import AccordionAdvancedExample from "@site/src/components/examples/AccordionAdvancedUsage"; + +export const AccordionComponent = ({ variant, children }) => ( + {children} +); + +```jsx + + This is summary + Hello + +``` + + + I am Accordion Summary + I am Accordion Content + + +##### Accordion Group + +Use this to give required styles for accordion. + +```jsx + + + Tab Navigate Me! + +
I have awesome accessibility
+
+
+ + You will love tabbing me! + + I am checkbox 1 + I am checkbox 2 + I am checkbox 3 + + + + This is summary + +
Hello
+
+
+
+``` + + + + Tab Navigate Me! + +
I have awesome accessibility
+
+
+ + You will love tabbing me! + + I am checkbox 1 + I am checkbox 2 + I am checkbox 3 + + + + This is summary + +
Hello
+
+
+
+ +##### Accordion Icon Position + +Specify the position of the icon in the accordion summary. Defaults to `right` + +```jsx + + This is summary + +
Hello
+
+
+``` + + + This is summary + +
Hello
+
+
+ +##### Accordion Custom Icon + +```jsx + + This is summary + +
Hello
+
+
+``` + + + ☝️} iconPosition="right"> + This is summary + + +
Hello
+
+
+ +##### Accordion Icon Transition and Animation + +```jsx + + +} + iconTransform={"rotate(45deg)"} + iconTransition={"transform 0.5s"} + iconPosition="right" + > + This is summary + + +
Hello
+
+
+``` + + + +} + iconTransform={"rotate(45deg)"} + iconTransition={"transform 0.5s"} + iconPosition="right" + > + This is summary + + +
Hello
+
+
+ +##### Accordion Advanced usage + +The accordion allows for really flexible usage + +```jsx +const [open, setOpen] = useState(false); +. +. +
+ { + setOpen(e.target.open); + }} + open={open} + > + This is summary + Hello + + + { + setOpen((prevValue) => !prevValue); + }} + /> +

Accordion is {open ? "open" : "closed"}

+
+
+``` + + + +### Props Reference + +#### Accordion + +Accordion also accepts native `
` element props. [Reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) + +| Attributes | Values | Default Value | Optional ? | +| :--------- | :----------------------: | :-----------: | ---------: | +| open | `true` , `false` | `false` | Yes | +| onToggle | `React OnToggle Handler` | - | Yes | + +#### AccordionSummary + +AccordionSummary also accepts native `` element props. [Reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary) + +| Attributes | Values | Default Value | Optional ? | +| :------------- | :--------------: | :--------------------------: | ---------: | +| Icon | React.ReactNode | undefined | Yes | +| iconPosition | 'left' , 'right' | 'right' | Yes | +| iconTransform | string | 'rotate(180deg)' | Yes | +| iconTransition | string | 'transform 0.2s ease-in-out' | Yes | + +#### AccordionContent + +AccordionContent accepts native `
` element props. [Reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div) + +#### AccordionGroup + +AccordionGroup accepts native `
` element props. [Reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div) diff --git a/docs/docs/components/icon.mdx b/docs/docs/components/icon.mdx new file mode 100644 index 0000000..6d80912 --- /dev/null +++ b/docs/docs/components/icon.mdx @@ -0,0 +1,35 @@ +# Icon + +### Quick start + +Here's a quick start guide to get started with the Icon component + +### Importing Component + +```jsx +import { Icon } from "@hover-design/react"; +``` + +### Code Snippets and Examples + +##### Icon Default + +`Icon` renders a `svg` element + +import "@hover-design/react/dist/style.css"; + +import { Icon } from "@hover-design/react"; + +```jsx + + + + +``` + + + + + + +Note Icon is still an experimental component. Its added to make working with icons easy, it does not provide any API as of V1 diff --git a/docs/src/components/examples/AccordionAdvancedUsage.tsx b/docs/src/components/examples/AccordionAdvancedUsage.tsx new file mode 100644 index 0000000..974d5ae --- /dev/null +++ b/docs/src/components/examples/AccordionAdvancedUsage.tsx @@ -0,0 +1,34 @@ +import React, { useState } from "react"; +import { + Accordion, + AccordionContent, + AccordionSummary, + Flex, + Switch, +} from "@hover-design/react"; +const AccordionAdvancedExample = ({}) => { + const [open, setOpen] = useState(false); + return ( +
+ { + setOpen(e.target.open); + }} + open={open} + > + This is summary + Hello + + + { + setOpen((prevValue) => !prevValue); + }} + /> +

Accordion is {open ? "open" : "closed"}

+
+
+ ); +}; +export default AccordionAdvancedExample; diff --git a/examples/vanilla-extract-react/package.json b/examples/vanilla-extract-react/package.json index 59f3390..c0010bb 100644 --- a/examples/vanilla-extract-react/package.json +++ b/examples/vanilla-extract-react/package.json @@ -12,8 +12,7 @@ "polished": "^4.1.3", "react": "^17.0.2", "react-dom": "^17.0.2", - "vite-tsconfig-paths": "^3.3.17", - "@hover-design/react": "*" + "vite-tsconfig-paths": "^3.3.17" }, "devDependencies": { "@types/react": "^18.0.6", diff --git a/lib/src/components/Accordion/Accordion.tsx b/lib/src/components/Accordion/Accordion.tsx new file mode 100644 index 0000000..e7b4e1e --- /dev/null +++ b/lib/src/components/Accordion/Accordion.tsx @@ -0,0 +1,23 @@ +import React, { ForwardRefRenderFunction, LegacyRef } from "react"; + +import { accordionThemeClass, detailsClass } from "./accordion.styles.css"; +import "./accordion.global.styles.css"; +import { IAccordionProps } from "./accordion.types"; + +const Accordion: ForwardRefRenderFunction< + HTMLDetailsElement, + IAccordionProps +> = ({ children, onToggle, className, ...nativeProps }, ref) => { + return ( +
| undefined} + onToggle={onToggle} + className={`${detailsClass} ${accordionThemeClass} ${className}`} + {...nativeProps} + > + {children} +
+ ); +}; +const AccordionWithRef = React.forwardRef(Accordion); +export { AccordionWithRef as Accordion }; diff --git a/lib/src/components/Accordion/AccordionContent/AccordionContent.tsx b/lib/src/components/Accordion/AccordionContent/AccordionContent.tsx new file mode 100644 index 0000000..8f7fe57 --- /dev/null +++ b/lib/src/components/Accordion/AccordionContent/AccordionContent.tsx @@ -0,0 +1,17 @@ +import React, { ForwardRefRenderFunction } from "react"; +import { accordionContentClass } from "../accordion.styles.css"; + +const AccordionContent: ForwardRefRenderFunction< + HTMLDivElement, + JSX.IntrinsicElements["div"] +> = ({ children, className, ...nativeProps }) => { + return ( +
+ {children} +
+ ); +}; + +const AccordionContentWithRef = React.forwardRef(AccordionContent); + +export { AccordionContentWithRef as AccordionContent }; diff --git a/lib/src/components/Accordion/AccordionGroup/AccordionGroup.tsx b/lib/src/components/Accordion/AccordionGroup/AccordionGroup.tsx new file mode 100644 index 0000000..8b561c1 --- /dev/null +++ b/lib/src/components/Accordion/AccordionGroup/AccordionGroup.tsx @@ -0,0 +1,20 @@ +import React, { ForwardRefRenderFunction } from "react"; +import { accordionGroupClass } from "../accordion.styles.css"; + +const AccordionGroup: ForwardRefRenderFunction< + HTMLDivElement, + JSX.IntrinsicElements["div"] +> = ({ children, className, ...nativeProps }, ref) => { + return ( +
+ {children} +
+ ); +}; + +const AccordionGroupWithRef = React.forwardRef(AccordionGroup); +export { AccordionGroupWithRef as AccordionGroup }; diff --git a/lib/src/components/Accordion/AccordionSummary/AccordionSummary.tsx b/lib/src/components/Accordion/AccordionSummary/AccordionSummary.tsx new file mode 100644 index 0000000..37676fc --- /dev/null +++ b/lib/src/components/Accordion/AccordionSummary/AccordionSummary.tsx @@ -0,0 +1,71 @@ +import { assignInlineVars } from "@vanilla-extract/dynamic"; +import React, { ForwardRefRenderFunction } from "react"; +import { Flex } from "src/components/Flex"; +import ChevronIcon from "src/components/_internal/Icons/Chevron"; +import { + accordionIconClass, + accordionThemeVars, + summaryClass, +} from "../accordion.styles.css"; +import { IAccordionSummaryProps } from "./accordionSummary.types"; + +const DefaultAccordionSummaryIcon: React.FC<{ + iconPosition: IAccordionSummaryProps["iconPosition"]; +}> = () => { + return ; +}; +const AccordionSummary: ForwardRefRenderFunction< + HTMLElement, + IAccordionSummaryProps +> = ( + { + children, + Icon, + iconPosition = "right", + iconTransform = "rotate(180deg)", + iconTransition = "transform 0.2s ease-in-out", + className, + style, + ...nativeProps + }, + ref +) => { + const summaryStyle = Object.assign( + assignInlineVars({ + [accordionThemeVars.accordionTransition]: iconTransition, + [accordionThemeVars.accordionTransform]: iconTransform, + }), + style + ); + const SummaryIcon = () => { + const accordionIconStyle = accordionIconClass({ position: iconPosition }); + + return ( + + {Icon || } + + ); + }; + + return ( + + {iconPosition === "left" && SummaryIcon()} + {children} + {iconPosition === "right" && SummaryIcon()} + + ); +}; + +const AccordionSummaryWithRef = React.forwardRef(AccordionSummary); + +export { AccordionSummaryWithRef as AccordionSummary }; diff --git a/lib/src/components/Accordion/AccordionSummary/accordionSummary.types.ts b/lib/src/components/Accordion/AccordionSummary/accordionSummary.types.ts new file mode 100644 index 0000000..58c6c47 --- /dev/null +++ b/lib/src/components/Accordion/AccordionSummary/accordionSummary.types.ts @@ -0,0 +1,11 @@ +import React from "react"; + +export interface IAccordionSummaryCustomProps { + iconPosition?: "left" | "right"; + Icon?: React.ReactNode; + iconTransform?: string; + iconTransition?: string; +} + +export type IAccordionSummaryProps = JSX.IntrinsicElements["summary"] & + IAccordionSummaryCustomProps; diff --git a/lib/src/components/Accordion/accordion.global.styles.css.ts b/lib/src/components/Accordion/accordion.global.styles.css.ts new file mode 100644 index 0000000..12f3a00 --- /dev/null +++ b/lib/src/components/Accordion/accordion.global.styles.css.ts @@ -0,0 +1,56 @@ +import { globalStyle } from "@vanilla-extract/css"; +import { + accordionGroupClass, + accordionThemeVars, + detailsClass, +} from "./accordion.styles.css"; + +globalStyle(`${detailsClass} > summary::-webkit-details-marker`, { + display: "none", +}); +globalStyle(`${detailsClass} > summary`, { + listStyle: "none", +}); +globalStyle(`${detailsClass}[open] > summary > [data-attribute="chevron"]`, { + transform: accordionThemeVars.accordionTransform, +}); + +globalStyle(`${detailsClass}[open] > summary`, { + backgroundColor: "hsla(216, 17%, 17%, 0.1)", + borderBottom: "1px solid hsla(0, 0%, 85%, 1)", +}); + +globalStyle(`${accordionGroupClass} > ${detailsClass}:first-child`, { + borderRadius: "4px 4px 0px 0px", +}); +globalStyle(`${accordionGroupClass} > ${detailsClass}:last-child`, { + borderRadius: "0px 0px 4px 4px", +}); +globalStyle(`${accordionGroupClass} > ${detailsClass}:first-child > summary`, { + borderRadius: "4px 4px 0px 0px", +}); +globalStyle(`${accordionGroupClass} > ${detailsClass}:last-child > summary`, { + borderRadius: "0px 0px 4px 4px", +}); +globalStyle( + `${accordionGroupClass} > ${detailsClass}:not(:first-child):not(:last-child)`, + { + borderRadius: "0px 0px 0px 0px", + } +); + +globalStyle( + `${accordionGroupClass} > ${detailsClass}:not(:first-child):not(:last-child) > summary`, + { + borderRadius: "0px 0px 0px 0px", + } +); +globalStyle(`${accordionGroupClass} > ${detailsClass}:not(:only-child)`, { + borderBottom: "1px solid hsla(0, 0%, 85%, 1)", +}); +globalStyle( + `${accordionGroupClass} > ${detailsClass}:not(:only-child) > summary`, + { + borderBottom: "1px solid hsla(0, 0%, 85%, 1)", + } +); diff --git a/lib/src/components/Accordion/accordion.styles.css.ts b/lib/src/components/Accordion/accordion.styles.css.ts new file mode 100644 index 0000000..96d496b --- /dev/null +++ b/lib/src/components/Accordion/accordion.styles.css.ts @@ -0,0 +1,39 @@ +import { createTheme, style } from "@vanilla-extract/css"; +import { IAccordionTheme } from "./accordion.types"; +import { recipe } from "@vanilla-extract/recipes"; + +export const [accordionThemeClass, accordionThemeVars]: IAccordionTheme = + createTheme({ + accordionTransform: "rotate(180deg)", + accordionTransition: "transform 0.2s ease-in-out", + }); + +export const accordionGroupClass = style({}); +export const detailsClass = style({}); +export const summaryOpenClass = style({}); +export const summaryClass = style({ + cursor: "pointer", + padding: "18px 16px", + + display: "flex", +}); + +export const accordionContentClass = style({ + padding: "16px", +}); +export const accordionIconClass = recipe({ + base: { + marginRight: "8px", + transition: accordionThemeVars.accordionTransition, + }, + variants: { + position: { + right: { + marginLeft: "auto", + }, + left: { + marginLeft: 0, + }, + }, + }, +}); diff --git a/lib/src/components/Accordion/accordion.types.ts b/lib/src/components/Accordion/accordion.types.ts new file mode 100644 index 0000000..f637566 --- /dev/null +++ b/lib/src/components/Accordion/accordion.types.ts @@ -0,0 +1,9 @@ +export interface IAccordionTypes { + Summary: React.FC<{}>; +} +export type IAccordionTheme = [ + string, + { accordionTransition: string; accordionTransform: string } +]; + +export type IAccordionProps = JSX.IntrinsicElements["details"]; diff --git a/lib/src/components/Accordion/index.ts b/lib/src/components/Accordion/index.ts new file mode 100644 index 0000000..e2674d9 --- /dev/null +++ b/lib/src/components/Accordion/index.ts @@ -0,0 +1,4 @@ +export * from "./Accordion"; +export * from "./AccordionContent/AccordionContent"; +export * from "./AccordionSummary/AccordionSummary"; +export * from "./AccordionGroup/AccordionGroup"; diff --git a/lib/src/components/Icon/Icon.tsx b/lib/src/components/Icon/Icon.tsx new file mode 100644 index 0000000..1e99801 --- /dev/null +++ b/lib/src/components/Icon/Icon.tsx @@ -0,0 +1,35 @@ +import React, { ForwardRefRenderFunction } from "react"; +import { IIconProps } from "./icon.type"; + +const Icon: ForwardRefRenderFunction = ({ + children, + xmlns = "http://www.w3.org/2000/svg", + width = "24", + height = "24", + viewBox = "0 0 24 24", + strokeWidth = "2", + stroke = "currentColor", + fill = "none", + strokeLinecap = "round", + strokeLinejoin = "round", + ...nativeProps +}) => { + return ( + + {children} + + ); +}; + +export { Icon }; diff --git a/lib/src/components/Icon/icon.type.ts b/lib/src/components/Icon/icon.type.ts new file mode 100644 index 0000000..1e1cf7e --- /dev/null +++ b/lib/src/components/Icon/icon.type.ts @@ -0,0 +1 @@ +export type IIconProps = React.SVGProps; diff --git a/lib/src/components/Icon/index.ts b/lib/src/components/Icon/index.ts new file mode 100644 index 0000000..998237c --- /dev/null +++ b/lib/src/components/Icon/index.ts @@ -0,0 +1 @@ +export * from "./Icon"; diff --git a/lib/src/components/_internal/Icons/Chevron.tsx b/lib/src/components/_internal/Icons/Chevron.tsx new file mode 100644 index 0000000..43c8279 --- /dev/null +++ b/lib/src/components/_internal/Icons/Chevron.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import { Icon } from "src/components/Icon/Icon"; +import { IIconProps } from "src/components/Icon/icon.type"; + +const ChevronIcon: React.FC = (props) => { + return ( + + + + + ); +}; + +export default ChevronIcon; diff --git a/lib/src/index.ts b/lib/src/index.ts index 8dfd88f..d826dff 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -6,6 +6,9 @@ export * from "./components/List"; export * from "./components/ListItem"; export * from "./components/reset"; export * from "./components/Switch"; -export * from "./components/TextArea"; +export * from "./components/Accordion"; export * from "./components/Divider"; +export * from "./components/Icon"; +export * from "./components/TextArea"; export * from "./components/Tab"; +