diff --git a/apps/docs/.eslintrc.json b/apps/docs/.eslintrc.json index 05eb0d577..82295d8eb 100644 --- a/apps/docs/.eslintrc.json +++ b/apps/docs/.eslintrc.json @@ -23,7 +23,8 @@ "IconSpecTable": true, "PreviewComponent": true, "MigrateGuide": true, - "PackageInstallation": true + "PackageInstallation": true, + "PropTable": true }, "overrides": [ { diff --git a/apps/docs/app/layout.css b/apps/docs/app/layout.css index c1a8a1c54..ccc7c99b3 100644 --- a/apps/docs/app/layout.css +++ b/apps/docs/app/layout.css @@ -23,7 +23,7 @@ html { } main { - padding-block: var(--hd-space-4) var(--hd-space-8); + padding-block: var(--hd-space-8); flex: 1 1 auto; min-width: 0; order: 1; diff --git a/apps/docs/app/lib/getComponentDetails.ts b/apps/docs/app/lib/getComponentDetails.ts index bffcb7faf..a99db4158 100644 --- a/apps/docs/app/lib/getComponentDetails.ts +++ b/apps/docs/app/lib/getComponentDetails.ts @@ -43,6 +43,7 @@ function getMDXData(dir: string) { const { frontmatter, content } = await readMDXFile(path.join(dir, file)); const slug = path.basename(file, path.extname(file)); + return { slug, frontmatter, diff --git a/apps/docs/app/ui/components/previewComponent/previewComponent.css b/apps/docs/app/ui/components/previewComponent/previewComponent.css index b8661bbb3..73b5c28b8 100644 --- a/apps/docs/app/ui/components/previewComponent/previewComponent.css +++ b/apps/docs/app/ui/components/previewComponent/previewComponent.css @@ -26,7 +26,7 @@ .hd-component-wrapper__action { position: absolute; - inset: 1rem 1rem 0 auto; + inset: 1rem 1rem auto auto; z-index: 100; } diff --git a/apps/docs/app/ui/components/propTable/PropTable.tsx b/apps/docs/app/ui/components/propTable/PropTable.tsx new file mode 100644 index 000000000..ae885064a --- /dev/null +++ b/apps/docs/app/ui/components/propTable/PropTable.tsx @@ -0,0 +1,93 @@ +import { promises as fs } from "fs"; +import path from "path"; +import type { PropItem } from "react-docgen-typescript/lib/parser"; +import type { ComponentDocWithGroups } from "@/scripts/generateComponentData.mjs"; +import Collapsible from "@/components/collapsible/Collapsible.tsx"; +import Title from "@/components/title/Title.tsx"; + +import "./propTable.css"; + +export interface PropTableProps { + component: string; +} + +interface PropItemTypeValue { + name: string; + value?: Array<{ value: string; name: string }>; + raw?: string; +} + +const filePath = path.join(process.cwd(), "datas", "components"); + +const getType = (type: PropItemTypeValue) => { + const handler: { + [key: string]: (type: PropItemTypeValue) => string; + } = { + enum: t => + t.value ? t.value.map(item => item.value.replace(/'/g, "")).join(" \\| ") : "", + union: t => t.value ? t.value.map(item => item.name).join(" \\| ") : "" + }; + if (typeof handler[type.name] === "function") { + return handler[type.name](type).replace(/\|/g, ""); + } + + return type.name; +}; + +const renderRow = (key: string, prop: PropItem) => { + const { name, type, defaultValue, required, description } = prop; + + return ( + + {name} + {getType(type)} + {defaultValue?.value} + {required ? "✓" : "✗"} + {description} + + ); +}; + +export default async function PropTable({ component }: PropTableProps) { + const file = await fs.readFile(filePath + `/${component}.json`, "utf8"); + const data = JSON.parse(file); + + return data.map((item: ComponentDocWithGroups) => { + const { description, displayName, groups } = item; + + return ( + <> +

{displayName}

+

{description}

+ {Object.keys(groups).map(group => { + return ( + + {group} + + } + className="props__section" + > + + + + + + + + + + + + {Object.keys(groups[group]).map(key => renderRow(key, groups[group][key]))} + +
PropertyTypeDefaultRequiredDescription
+
+ ); + })} + + ); + }); +} diff --git a/apps/docs/app/ui/components/propTable/propTable.css b/apps/docs/app/ui/components/propTable/propTable.css new file mode 100644 index 000000000..578471498 --- /dev/null +++ b/apps/docs/app/ui/components/propTable/propTable.css @@ -0,0 +1,7 @@ +.props__section { + margin-top: var(--hd-space-1); +} + +.props__section .hd-collapsible__content { + margin-top: var(--hd-space-1); +} diff --git a/apps/docs/app/ui/layout/aside/aside.css b/apps/docs/app/ui/layout/aside/aside.css index 837601c06..0bf3b13c2 100644 --- a/apps/docs/app/ui/layout/aside/aside.css +++ b/apps/docs/app/ui/layout/aside/aside.css @@ -58,7 +58,7 @@ /* making sure that if a user toggles the list and resize his browser the list is still visible */ /* We could do this in JS but I think that fixes the issue */ - display: flex!important; + display: flex !important; } .hd-aside__list--closed { @@ -117,7 +117,7 @@ left: 0; z-index: 1; border-radius: var(--hd-space-1); - transition: top .25s cubic-bezier(0,1,.5,1); + transition: top .25s cubic-bezier(0, 1, .5, 1); display: none; } diff --git a/apps/docs/app/ui/tokens/preview/preview.css b/apps/docs/app/ui/tokens/preview/preview.css index 5d5fbcddf..cde6d319c 100644 --- a/apps/docs/app/ui/tokens/preview/preview.css +++ b/apps/docs/app/ui/tokens/preview/preview.css @@ -3,7 +3,9 @@ width: var(--hd-space-5); height: var(--hd-space-3); border-radius: 0; - float: right; + + /* float: right; */ + display: inline-block; background-color: var(--hd-color-primary-surface); } @@ -18,7 +20,7 @@ .hd-preview--font { align-items: center; background-color: transparent; - display: flex; + display: inline-flex; font-size: 1.5rem; height: auto; justify-content: flex-end; diff --git a/apps/docs/app/ui/tokens/table/IconSpecTable.tsx b/apps/docs/app/ui/tokens/table/IconSpecTable.tsx index e4b1368de..45042d04d 100644 --- a/apps/docs/app/ui/tokens/table/IconSpecTable.tsx +++ b/apps/docs/app/ui/tokens/table/IconSpecTable.tsx @@ -1,8 +1,6 @@ -"use client"; +import Table from "@/components/table/Table"; -import { Cell, Column, Row, Table as TableRA, TableBody, TableHeader } from "react-aria-components"; - -import "./table.css"; +import "./tokenTable.css"; interface IconSpecTableProps { data: { @@ -15,34 +13,11 @@ interface IconSpecTableProps { } const IconSpecTable = ({ data }: IconSpecTableProps) => { - const sizes = ["sm", "md", "lg"]; - - const listItems = data?.map(row => { - return ( - - {row.name} - {sizes.map(size => ( - - {row[size]} - - ))} - - ); - }); - - return ( - - - Anatomy - Small - Medium - Large - - - {listItems} - - - ); + return ; }; export default IconSpecTable; diff --git a/apps/docs/app/ui/tokens/table/Table.tsx b/apps/docs/app/ui/tokens/table/Table.tsx deleted file mode 100644 index d4ebe9621..000000000 --- a/apps/docs/app/ui/tokens/table/Table.tsx +++ /dev/null @@ -1,50 +0,0 @@ -"use client"; - -import { Cell, Column, Row, Table as TableRA, TableBody, TableHeader } from "react-aria-components"; - -import Preview from "@/app/ui/tokens/preview/Preview"; -import Code from "@/components/code/Code"; - -import "./table.css"; - -interface TableProps { - category: string; - noPreview?: boolean; - data: { - name: string; - value: string; - }[]; -} - -const Table = ({ category, data, noPreview }: TableProps) => { - const listItems = data?.map(token => { - const { name, value } = token; - - return ( - - - {`--${name}`} - - {value} - {!noPreview && - - } - - ); - }); - - return ( - - - Name - Value - {!noPreview && Preview} - - - {listItems} - - - ); -}; - -export default Table; diff --git a/apps/docs/app/ui/tokens/table/Table.stories.tsx b/apps/docs/app/ui/tokens/table/TokenTable.stories.tsx similarity index 80% rename from apps/docs/app/ui/tokens/table/Table.stories.tsx rename to apps/docs/app/ui/tokens/table/TokenTable.stories.tsx index 2aa8f577b..cd080f86c 100644 --- a/apps/docs/app/ui/tokens/table/Table.stories.tsx +++ b/apps/docs/app/ui/tokens/table/TokenTable.stories.tsx @@ -1,11 +1,11 @@ import type { Meta, StoryObj } from "@storybook/react"; -import Table from "./Table"; +import TokenTable from "./TokenTable.tsx"; const meta = { - title: "app/tokens/Table", - component: Table -} satisfies Meta; + title: "app/tokens/TokenTable", + component: TokenTable +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/app/ui/tokens/table/TokenTable.tsx b/apps/docs/app/ui/tokens/table/TokenTable.tsx new file mode 100644 index 000000000..8276385e3 --- /dev/null +++ b/apps/docs/app/ui/tokens/table/TokenTable.tsx @@ -0,0 +1,36 @@ +import Table from "@/components/table/Table"; + +import Preview from "@/app/ui/tokens/preview/Preview"; +import Code from "@/components/code/Code"; + +import "./tokenTable.css"; + +interface TableProps { + category: string; + noPreview?: boolean; + data: { + name: string; + value: string; + }[]; +} + +const TokenTable = ({ category, data, noPreview }: TableProps) => { + const formattedData = data.map(token => { + const { name, value } = token; + + return { + name: {`--${name}`}, + value: value, + preview: !noPreview && + }; + }); + + + return
; +}; + +export default TokenTable; diff --git a/apps/docs/app/ui/tokens/table/TypographyTable.tsx b/apps/docs/app/ui/tokens/table/TypographyTable.tsx index cbc5618ef..c9a2e68f6 100644 --- a/apps/docs/app/ui/tokens/table/TypographyTable.tsx +++ b/apps/docs/app/ui/tokens/table/TypographyTable.tsx @@ -1,11 +1,15 @@ -"use client"; - -import React from "react"; import clsx from "clsx"; -import { groupItemsByProperties, groupItemsByPropertiesAndSizes, type Size, type TokenData } from "@/app/lib/getTypographyTokens"; -import { TypographyTableRow } from "./TypographyTableRow"; +import Table from "@/components/table/Table"; + +import { + groupItemsByProperties, + groupItemsByPropertiesAndSizes, + type Size, + type TokenData +} from "@/app/lib/getTypographyTokens"; +import { typographyTableRow } from "./TypographyTableRow"; -import "./table.css"; +import "./tokenTable.css"; // maps the raw token list of a list filtered by property function transformDataToTokenData(inputData: Record): TokenData { @@ -31,36 +35,36 @@ const TypographyTable = ({ type, data }: TypographyTableProps) => { const hasNoSizes = type === "overline"; const tokenData = transformDataToTokenData(data); - const listItems = hasNoSizes ? generateSizelessRows(tokenData, type) : generateSizeRows(tokenData, type); + const listItems = hasNoSizes ? [generateSizelessRows(tokenData, type)] : generateSizeRows(tokenData, type); - return ( -
- - - {!hasNoSizes && } - - - - - - {listItems} - -
SizeValuesPreview
- ); + return (); }; function generateSizeRows(tokenData: TokenData, type: string) { const filteredData = groupItemsByPropertiesAndSizes(tokenData, type); return Object.keys(filteredData).map(size => { - return ; + return typographyTableRow( + type, + filteredData[size as keyof typeof filteredData]!, + size as Size + ); }); } function generateSizelessRows(tokenData: TokenData, type: string) { const properties = groupItemsByProperties(tokenData, type); - return ; + return typographyTableRow(type, properties!); } export default TypographyTable; diff --git a/apps/docs/app/ui/tokens/table/TypographyTableRow.tsx b/apps/docs/app/ui/tokens/table/TypographyTableRow.tsx index d8547b7d3..3e7fd31f6 100644 --- a/apps/docs/app/ui/tokens/table/TypographyTableRow.tsx +++ b/apps/docs/app/ui/tokens/table/TypographyTableRow.tsx @@ -2,15 +2,10 @@ import type { ComponentProps, ReactNode } from "react"; import TypographyPreview from "../preview/TypographyPreview"; import Code from "@/components/code/Code"; import type { FontProperties, Size } from "@/app/lib/getTypographyTokens"; -import "./table.css"; -interface TypographyTableRowProps { - type: string; - properties: FontProperties; - size?: Size; -} +import "./tokenTable.css"; -export function TypographyTableRow({ type, properties, size }: TypographyTableRowProps) { +export function typographyTableRow(type: string, properties: FontProperties, size?: Size) { const { fontFamily, fontSize, @@ -29,101 +24,97 @@ export function TypographyTableRow({ type, properties, size }: TypographyTableRo }; } - return ( - - {size && } - - - - ); + return ({ + name: size, + value: , + preview: + }); } interface PropertiesCellProps { properties: FontProperties; } -function PropertiesCell({ properties } : PropertiesCellProps) { +function PropertiesCell({ properties }: PropertiesCellProps) { return ( - +
{size} - -
- - - {properties.fontSize && ( - - )} - {properties.fontWeight && ( - - )} - {properties.lineHeight && ( - - )} - {properties.fontFamily && ( - - )} - {properties.topOffset && ( - Top Offset1} - value={properties.topOffset.value} - /> - )} - {properties.bottomOffset && ( - Bottom Offset1} - value={properties.bottomOffset.value} - /> - )} - -
-
+ + {properties.fontSize && ( + + )} + {properties.fontWeight && ( + + )} + {properties.lineHeight && ( + + )} + {properties.fontFamily && ( + + )} + {properties.topOffset && ( + Top Offset1} + value={properties.topOffset.value} + /> + )} + {properties.bottomOffset && ( + Bottom Offset1} + value={properties.bottomOffset.value} + /> + )} + +
); } -interface PropertyRowProps extends Omit, "children">{ +interface PropertyRowProps extends Omit, "children"> { tokenName: string; displayName: ReactNode; value: string; } function PropertyRow({ tokenName, displayName, value, ...rest }: PropertyRowProps) { - const tokenValue = value; - return ( @@ -133,7 +124,7 @@ function PropertyRow({ tokenName, displayName, value, ...rest }: PropertyRowProp {tokenName} - {tokenValue} + {value} ); diff --git a/apps/docs/app/ui/tokens/table/TypographyVariantTable.tsx b/apps/docs/app/ui/tokens/table/TypographyVariantTable.tsx index 0f9088dae..87f55ce1e 100644 --- a/apps/docs/app/ui/tokens/table/TypographyVariantTable.tsx +++ b/apps/docs/app/ui/tokens/table/TypographyVariantTable.tsx @@ -1,10 +1,8 @@ -"use client"; - -import React from "react"; import TypographyPreview from "@/app/ui/tokens/preview/TypographyPreview"; import Code from "@/components/code/Code"; +import Table from "@/components/table/Table"; -import "./table.css"; +import "./tokenTable.css"; interface TypographyVariantTableProps { data: Record; @@ -27,37 +25,20 @@ const TypographyVariantTable = ({ type, data }: TypographyVariantTableProps) => const listItems = filteredDataByWeightVariation.map(item => { const fontWeight = item.value; - return ( - - - {item.name} - - {`--${item.name}`} - - - {item.value} - - - - - - - ); + return { + name: {`--${item.name}`}, + value: fontWeight, + preview: + }; }); return ( - - - - - - - - - - {listItems} - -
NameValuePreview
+ ); }; diff --git a/apps/docs/app/ui/tokens/table/table.css b/apps/docs/app/ui/tokens/table/tokenTable.css similarity index 60% rename from apps/docs/app/ui/tokens/table/table.css rename to apps/docs/app/ui/tokens/table/tokenTable.css index cd275e8f4..56f42626e 100644 --- a/apps/docs/app/ui/tokens/table/table.css +++ b/apps/docs/app/ui/tokens/table/tokenTable.css @@ -1,44 +1,3 @@ -.hd-table { - width: 100%; - outline: none; - border-spacing: 0; - align-self: start; -} - -.hd-table__column { - padding: var(--hd-space-2) var(--hd-space-1); - text-align: left; - color: var(--hd-color-neutral-text-weakest); - font-size: 0.8125rem; - font-style: normal; - font-weight: 400; - line-height: 0.8125rem; -} - -.hd-table__column:first-child { - padding-left: 0; -} - -.hd-table__column:last-child, -.hd-table__cell:last-child { - text-align: right; -} - -.hd-table__column--size { - min-width: 3rem; -} - -.hd-table__cell { - font-family: var(--hd-mono-font-family); - font-size: 0.8125rem; - padding: var(--hd-space-1); - border-bottom: var(--hd-border-size) solid var(--hd-color-neutral-border); -} - -.hd-table__row > .hd-table__cell:first-child { - padding-left: 0; -} - /* TypoTable */ .hd-table__cell.hd-typo__cell { text-align: left; @@ -72,7 +31,7 @@ } /* Has No Sizes */ -.hd-typo-table--has-no-sizes .hd-typo__cell{ +.hd-typo-table--has-no-sizes .hd-typo__cell { padding: 0; } diff --git a/apps/docs/app/ui/tokens/tableSection/TableSection.tsx b/apps/docs/app/ui/tokens/tableSection/TableSection.tsx index 7ed27b1d0..b5d44607c 100644 --- a/apps/docs/app/ui/tokens/tableSection/TableSection.tsx +++ b/apps/docs/app/ui/tokens/tableSection/TableSection.tsx @@ -1,7 +1,7 @@ "use client"; import React from "react"; -import Table from "@/app/ui/tokens/table/Table"; +import TokenTable from "@/app/ui/tokens/table/TokenTable.tsx"; import "@hopper-ui/tokens/fonts.css"; @@ -25,7 +25,7 @@ const TableSection = ({ tokens, categories, excludedCategories, categoryKey }: T }); return
-
+ ; }; diff --git a/apps/docs/components/collapsible/Collapsible.stories.tsx b/apps/docs/components/collapsible/Collapsible.stories.tsx new file mode 100644 index 000000000..5f3d09385 --- /dev/null +++ b/apps/docs/components/collapsible/Collapsible.stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import Collasible from "./Collapsible.tsx"; + + +const meta = { + title: "components/Collasible", + component: Collasible +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + title: "Label", + children: "Conent of the collapsible" + } +}; diff --git a/apps/docs/components/collapsible/Collapsible.tsx b/apps/docs/components/collapsible/Collapsible.tsx new file mode 100644 index 000000000..169dc64de --- /dev/null +++ b/apps/docs/components/collapsible/Collapsible.tsx @@ -0,0 +1,52 @@ +"use client"; + +import clsx from "clsx"; +import { type ReactNode, useRef, useState } from "react"; +import { ToggleButton } from "react-aria-components"; +import { Icon, CollapseIcon } from "@/components/icon"; + +import "./collapsible.css"; + +export interface CollapsibleProps { + children: ReactNode; + title: ReactNode; + label?: string; + isOpen?: boolean; + className?: string; +} + +const Collapsible = ({ children, title, label, isOpen = false, className }: CollapsibleProps) => { + const [open, setOpen] = useState(isOpen); + const contentRef = useRef(null); + + let styles = { height: "0px" }; + + if (contentRef.current && open) { + styles = { height: contentRef.current.scrollHeight + "px" }; + } + + const toggle = () => { + setOpen(!open); + }; + + return ( +
+ + {title} + + +
+
{children}
+
+
+ ); +}; + +export default Collapsible; diff --git a/apps/docs/components/collapsible/collapsible.css b/apps/docs/components/collapsible/collapsible.css new file mode 100644 index 000000000..6d0282a60 --- /dev/null +++ b/apps/docs/components/collapsible/collapsible.css @@ -0,0 +1,52 @@ +/* Collapse */ +.hd-collapsible__trigger { + --collapsible-trigger-background: transparent; + + font-family: var(--hd-default-font-family); + font-size: var(--hd-default-font-size); + line-height: 1.35; + color: var(--hd-color-neutral-text); + display: flex; + width: 100%; + justify-content: space-between; + align-items: center; + padding-block: var(--hd-space-2); + padding-inline: var(--hd-space-2); + border: none; + border-radius: 0.5rem; + background: var(--collapsible-trigger-background); + box-shadow: none; + transition: box-shadow 0.15s ease-in-out; + margin-inline: calc(var(--hd-space-2) * -1); +} + +.hd-collapsible__trigger[data-hovered="true"] { + --collapsible-trigger-background: var(--hd-color-neutral-surface); + + cursor: pointer; + box-shadow: 0 0.25rem 0.625rem 0.25rem rgb(60 60 60 / 8%); +} + +.hd-collapsible__trigger .hd-title { + margin-block: 0; + margin-inline: 0; +} + +.hd-collapsible__trigger[data-focused="true"], +.hd-collapsible__trigger[data-focus-visible="true"] { + outline: none; +} + +.hd-collapsible__trigger[data-focus-visible="true"] { + box-shadow: var(--hd-focus-ring); +} + +.hd-collapsible__content { + padding-block: var(--hd-space-1); +} + +.hd-collapsible__content-parent { + height: 0; + overflow: hidden; + transition: height ease 0.15s; +} diff --git a/apps/docs/components/icon/assets/collapse.svg b/apps/docs/components/icon/assets/collapse.svg new file mode 100644 index 000000000..b69aec16d --- /dev/null +++ b/apps/docs/components/icon/assets/collapse.svg @@ -0,0 +1,6 @@ + + + + diff --git a/apps/docs/components/icon/index.tsx b/apps/docs/components/icon/index.tsx index 713ca09db..7364279df 100644 --- a/apps/docs/components/icon/index.tsx +++ b/apps/docs/components/icon/index.tsx @@ -12,7 +12,7 @@ import NpmIcon from "./assets/npm.svg"; import SelectArrowIcon from "./assets/select-arrow.svg"; import TokenIcon from "./assets/tokens.svg"; import TypescriptIcon from "./assets/typescript.svg"; - +import CollapseIcon from "./assets/collapse.svg"; import Icon, { type IconProps } from "./Icon.tsx"; export { @@ -29,6 +29,7 @@ export { NpmIcon, SelectArrowIcon, TokenIcon, + CollapseIcon, TypescriptIcon, Icon, IconProps diff --git a/apps/docs/components/mdx/components.tsx b/apps/docs/components/mdx/components.tsx index 2d87bd93c..a3c0695c7 100644 --- a/apps/docs/components/mdx/components.tsx +++ b/apps/docs/components/mdx/components.tsx @@ -5,7 +5,7 @@ import Card from "@/components/card/Card.tsx"; import NextImage from "@/components/image/Image.tsx"; import Pre from "@/components/pre/Pre.tsx"; import InlineCode from "@/components/code/InlineCode.tsx"; -import Table from "@/app/ui/tokens/table/Table.tsx"; +import TokenTable from "@/app/ui/tokens/table/TokenTable.tsx"; import TypographyTable from "@/app/ui/tokens/table/TypographyTable.tsx"; import TypographyVariantTable from "@/app/ui/tokens/table/TypographyVariantTable.tsx"; import { IconTable } from "@/app/ui/icons/iconTable/IconTable.tsx"; @@ -16,14 +16,18 @@ import Switcher from "@/app/ui/icons/switcher/Switcher.tsx"; import Title from "@/components/title/Title.tsx"; import MotionPreview from "@/components/motionPreview/MotionPreview.tsx"; import Footnote from "@/components/footnote/Footnote.tsx"; -import PackageInstallation, { type PackageInstallationProps } from "@/components/packageInstallation/PackageInstallation.tsx"; +import PackageInstallation, { + type PackageInstallationProps +} from "@/components/packageInstallation/PackageInstallation.tsx"; import type { PreviewComponentProps } from "@/app/ui/components/previewComponent/PreviewComponent.tsx"; import type { MigrateGuideProps } from "@/app/ui/components/migrateGuide/MigrateGuide.tsx"; +import type { PropTableProps } from "@/app/ui/components/propTable/PropTable.tsx"; type HeadingProps = React.DetailedHTMLProps, HTMLHeadingElement>; const PreviewComponent = dynamic(() => import("@/app/ui/components/previewComponent/PreviewComponent.tsx")); const MigrateGuide = dynamic(() => import("@/app/ui/components/migrateGuide/MigrateGuide.tsx")); +const PropTable = dynamic(() => import("@/app/ui/components/propTable/PropTable.tsx")); export const components = { Card, @@ -32,7 +36,7 @@ export const components = { pre: Pre, MotionPreview: MotionPreview, Footnote: Footnote, - Table: Table, + Table: TokenTable, TypographyTable: TypographyTable, TypographyVariantTable: TypographyVariantTable, IconTable: IconTable, @@ -49,6 +53,9 @@ export const components = { MigrateGuide: (props: MigrateGuideProps) => { return ; }, + PropTable: (props: PropTableProps) => { + return ; + }, h1: (props: HeadingProps) => { return ; }, diff --git a/apps/docs/components/table/Table.stories.tsx b/apps/docs/components/table/Table.stories.tsx new file mode 100644 index 000000000..71eba3345 --- /dev/null +++ b/apps/docs/components/table/Table.stories.tsx @@ -0,0 +1,50 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import Table from "./Table"; +import Code from "@/components/code/Code"; +import Preview from "@/app/ui/tokens/preview/Preview.tsx"; + +const meta = { + title: "components/Table", + component: Table +} satisfies Meta<typeof Table>; + +export default meta; +type Story = StoryObj<typeof meta>; + +export const Default: Story = { + args: { + head: ["Position", "Mass", "Symbol", "Name"], + data: [ + { position: 6, mass: 12.011, symbol: "C", name: "Carbon" }, + { position: 7, mass: 14.007, symbol: "N", name: "Nitrogen" }, + { position: 39, mass: 88.906, symbol: "Y", name: "Yttrium" }, + { position: 56, mass: 137.33, symbol: "Ba", name: "Barium" }, + { position: 58, mass: 140.12, symbol: "Ce", name: "Cerium" } + ] + } +}; + +export const Tokens: Story = { + args: { + lastColumnAlignment: "right", + head: ["Token", "Value", "Preview"], + data: [ + { + name: <Code value="--hop-sapphire-200">--hop-sapphire-200</Code>, + value: "#95b1ff", + preview: <Preview value="#95b1ff" name="--hop-sapphire-200" category="react" /> + }, + { + name: <Code value="--hop-primary-surface-disabled">--hop-primary-surface-disabled</Code>, + value: "#95b1ff", + preview: <Preview value="#95b1ff" name="--hop-primary-surface-disabled" category="react" /> + }, + { + name: <Code value="--hop-primary-surface-disabled">--hop-primary-surface-disabled</Code>, + value: "#2040c7", + preview: <Preview value="#2040c7" name="--hop-primary-surface-disabled" category="react" /> + } + ] + } +}; diff --git a/apps/docs/components/table/Table.tsx b/apps/docs/components/table/Table.tsx new file mode 100644 index 000000000..9a84d3db0 --- /dev/null +++ b/apps/docs/components/table/Table.tsx @@ -0,0 +1,68 @@ +"use client"; + +import clsx from "clsx"; +import { Cell, Column, Row, Table as TableRA, TableBody, TableHeader } from "react-aria-components"; +import type { ReactNode } from "react"; + +import "./table.css"; + +interface dataType { + [key: string]: string | number | boolean | undefined | null | ReactNode; +} + +interface TableProps { + head: (string | boolean)[]; + data: dataType[]; + lastColumnAlignment?: "left" | "right"; + "ariaLabel"?: string; + className?: string; +} + +function generateUniqueKey() { + return `${Date.now()}-${Math.random()}`; +} + +const Table = ({ data, head, lastColumnAlignment = "left", ariaLabel = "standard table", className }: TableProps) => { + const textAlignRight = lastColumnAlignment === "right"; + const lastColumn = head.length - 1; + + const headItems = head.map((item, index) => { + return ( + <Column isRowHeader + key={`table-body-${generateUniqueKey()}`} + className={clsx("hd-table__column", { "hd-table__colum--right": index === lastColumn && textAlignRight })} + > + {item} + </Column> + ); + }); + + const dataItems = data.map(item => { + return ( + <Row key={`table-body-${generateUniqueKey()}`} className="hd-table__row"> + {Object.keys(item).map((key, index) => { + return ( + <Cell key={key} + className={clsx("hd-table__cell", { "hd-table__cell--right": index === lastColumn && textAlignRight })} + > + {item[key]} + </Cell> + ); + })} + </Row> + ); + }); + + return ( + <TableRA className={clsx("hd-table", className)} aria-label={ariaLabel}> + <TableHeader> + {headItems} + </TableHeader> + <TableBody> + {dataItems} + </TableBody> + </TableRA> + ); +}; + +export default Table; diff --git a/apps/docs/components/table/table.css b/apps/docs/components/table/table.css new file mode 100644 index 000000000..195fba20c --- /dev/null +++ b/apps/docs/components/table/table.css @@ -0,0 +1,40 @@ +.hd-table { + width: 100%; + outline: none; + border-spacing: 0; + align-self: start; +} + +.hd-table__column { + padding: var(--hd-space-2) var(--hd-space-1); + text-align: left; + color: var(--hd-color-neutral-text-weakest); + font-size: 0.8125rem; + font-style: normal; + font-weight: 400; + line-height: 0.8125rem; +} + +.hd-table__column:first-child { + padding-left: 0; +} + +.hd-table__column--size { + min-width: 3rem; +} + +.hd-table__cell { + font-family: var(--hd-mono-font-family); + font-size: 0.8125rem; + padding: var(--hd-space-1); + border-bottom: var(--hd-border-size) solid var(--hd-color-neutral-border); +} + +.hd-table__row > .hd-table__cell:first-child { + padding-left: 0; +} + +.hd-table__colum--right, +.hd-table__cell--right { + text-align: right; +} diff --git a/apps/docs/content/components/button.mdx b/apps/docs/content/components/button.mdx index 7c253f9dc..d7c844de4 100644 --- a/apps/docs/content/components/button.mdx +++ b/apps/docs/content/components/button.mdx @@ -17,6 +17,10 @@ import { Button } from "@hopper-ui/components"; ```tsx showLineNumbers <Button>save</Button> ``` +## Props +The table below contains all types of the props available in title component. + +<PropTable component="Button" /> ## Migration Notes <MigrateGuide src="buttons/docs/migration-notes" /> diff --git a/apps/docs/datas/components/Button.json b/apps/docs/datas/components/Button.json new file mode 100644 index 000000000..9b7263605 --- /dev/null +++ b/apps/docs/datas/components/Button.json @@ -0,0 +1 @@ +[{"tags":{},"filePath":"/Users/franck.gaudin/Devel/DS/wl-hopper/packages/components/src/buttons/src/Button.tsx","description":"Buttons are used to initialize an action. Button labels express what action will occur when the user interacts with it.\n\n[View Documentation](TODO)","displayName":"Button","methods":[],"props":{"variant":{"defaultValue":{"value":"\"primary\""},"description":"The visual style of the button.\n*","name":"variant","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"\"primary\" | \"secondary\" | \"danger\" | \"upsell\" | \"ghost-primary\" | \"ghost-secondary\" | \"ghost-danger\""}},"size":{"defaultValue":{"value":"\"md\""},"description":"A button can vary in size.","name":"size","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"ResponsiveProp<\"sm\" | \"md\">"}},"fluid":{"defaultValue":null,"description":"Whether or not the button takes up the width of its container.","name":"fluid","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"ResponsiveProp<boolean>"}},"isLoading":{"defaultValue":null,"description":"","name":"isLoading","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"boolean"}},"href":{"defaultValue":null,"description":"A URL to link to. Setting this makes the component render an `a` tag instead of a `button`","name":"href","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"target":{"defaultValue":null,"description":"The target window for the link.","name":"target","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"rel":{"defaultValue":null,"description":"The relationship between the linked resource and the current page. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).","name":"rel","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"form":{"defaultValue":null,"description":"The <form> element to associate the button with.\nThe value of this attribute must be the id of a <form> in the same document.","name":"form","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"slot":{"defaultValue":null,"description":"A slot name for the component. Slots allow the component to receive props from a parent component.\nAn explicit `null` value indicates that the local props completely override all props received from a parent.","name":"slot","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"SlotProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"SlotProps"}],"required":false,"type":{"name":"string"}},"style":{"defaultValue":null,"description":"The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state.","name":"style","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"StyleRenderProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"StyleRenderProps"}],"required":false,"type":{"name":"CSSProperties | ((values: ButtonRenderProps) => CSSProperties)"}},"children":{"defaultValue":null,"description":"The children of the component. A function may be provided to alter the children based on component state.","name":"children","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"RenderProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"RenderProps"}],"required":false,"type":{"name":"ReactNode | ((values: ButtonRenderProps) => ReactNode)"}},"isDisabled":{"defaultValue":null,"description":"Whether the button is disabled.","name":"isDisabled","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"boolean"}},"onPress":{"defaultValue":null,"description":"Handler that is called when the press is released over the target.","name":"onPress","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"}],"required":false,"type":{"name":"(e: PressEvent) => void"}},"onPressStart":{"defaultValue":null,"description":"Handler that is called when a press interaction starts.","name":"onPressStart","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"}],"required":false,"type":{"name":"(e: PressEvent) => void"}},"onPressEnd":{"defaultValue":null,"description":"Handler that is called when a press interaction ends, either\nover the target or when the pointer leaves the target.","name":"onPressEnd","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"}],"required":false,"type":{"name":"(e: PressEvent) => void"}},"onPressChange":{"defaultValue":null,"description":"Handler that is called when the press state changes.","name":"onPressChange","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"}],"required":false,"type":{"name":"(isPressed: boolean) => void"}},"onPressUp":{"defaultValue":null,"description":"Handler that is called when a press is released over the target, regardless of\nwhether it started on the target or not.","name":"onPressUp","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"}],"required":false,"type":{"name":"(e: PressEvent) => void"}},"autoFocus":{"defaultValue":null,"description":"Whether the element should receive focus on render.","name":"autoFocus","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusableProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusableProps"}],"required":false,"type":{"name":"boolean"}},"onFocus":{"defaultValue":null,"description":"Handler that is called when the element receives focus.","name":"onFocus","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"}],"required":false,"type":{"name":"(e: FocusEvent<Element, Element>) => void"}},"onBlur":{"defaultValue":null,"description":"Handler that is called when the element loses focus.","name":"onBlur","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"}],"required":false,"type":{"name":"(e: FocusEvent<Element, Element>) => void"}},"onFocusChange":{"defaultValue":null,"description":"Handler that is called when the element's focus status changes.","name":"onFocusChange","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"}],"required":false,"type":{"name":"(isFocused: boolean) => void"}},"onKeyDown":{"defaultValue":null,"description":"Handler that is called when a key is pressed.","name":"onKeyDown","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"KeyboardEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"KeyboardEvents"}],"required":false,"type":{"name":"(e: KeyboardEvent) => void"}},"onKeyUp":{"defaultValue":null,"description":"Handler that is called when a key is released.","name":"onKeyUp","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"KeyboardEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"KeyboardEvents"}],"required":false,"type":{"name":"(e: KeyboardEvent) => void"}},"aria-expanded":{"defaultValue":null,"description":"Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed.","name":"aria-expanded","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"}],"required":false,"type":{"name":"boolean | \"true\" | \"false\""}},"aria-haspopup":{"defaultValue":null,"description":"Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element.","name":"aria-haspopup","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"}],"required":false,"type":{"name":"boolean | \"dialog\" | \"menu\" | \"grid\" | \"true\" | \"false\" | \"listbox\" | \"tree\""}},"aria-controls":{"defaultValue":null,"description":"Identifies the element (or elements) whose contents or presence are controlled by the current element.","name":"aria-controls","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"}],"required":false,"type":{"name":"string"}},"aria-pressed":{"defaultValue":null,"description":"Indicates the current \"pressed\" state of toggle buttons.","name":"aria-pressed","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"}],"required":false,"type":{"name":"boolean | \"true\" | \"false\" | \"mixed\""}},"type":{"defaultValue":{"value":"'button'"},"description":"The behavior of the button when used in an HTML form.","name":"type","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"}],"required":false,"type":{"name":"\"button\" | \"submit\" | \"reset\""}},"excludeFromTabOrder":{"defaultValue":null,"description":"Whether to exclude the element from the sequential tab order. If true,\nthe element will not be focusable via the keyboard by tabbing. This should\nbe avoided except in rare scenarios where an alternative means of accessing\nthe element or its functionality via the keyboard is available.","name":"excludeFromTabOrder","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"FocusableDOMProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"FocusableDOMProps"}],"required":false,"type":{"name":"boolean"}},"id":{"defaultValue":null,"description":"The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id).","name":"id","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"DOMProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"DOMProps"}],"required":false,"type":{"name":"string"}},"aria-label":{"defaultValue":null,"description":"Defines a string value that labels the current element.","name":"aria-label","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"}],"required":false,"type":{"name":"string"}},"aria-labelledby":{"defaultValue":null,"description":"Identifies the element (or elements) that labels the current element.","name":"aria-labelledby","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"}],"required":false,"type":{"name":"string"}},"aria-describedby":{"defaultValue":null,"description":"Identifies the element (or elements) that describes the object.","name":"aria-describedby","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"}],"required":false,"type":{"name":"string"}},"aria-details":{"defaultValue":null,"description":"Identifies the element (or elements) that provide a detailed, extended description for the object.","name":"aria-details","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"}],"required":false,"type":{"name":"string"}},"formAction":{"defaultValue":null,"description":"The URL that processes the information submitted by the button.\nOverrides the action attribute of the button's form owner.","name":"formAction","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"formEncType":{"defaultValue":null,"description":"Indicates how to encode the form data that is submitted.","name":"formEncType","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"formMethod":{"defaultValue":null,"description":"Indicates the HTTP method used to submit the form.","name":"formMethod","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"formNoValidate":{"defaultValue":null,"description":"Indicates that the form is not to be validated when it is submitted.","name":"formNoValidate","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"boolean"}},"formTarget":{"defaultValue":null,"description":"Overrides the target attribute of the button's form owner.","name":"formTarget","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"value":{"defaultValue":null,"description":"The value associated with the button's name when it's submitted with the form data.","name":"value","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"onHoverStart":{"defaultValue":null,"description":"Handler that is called when a hover interaction starts.","name":"onHoverStart","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"}],"required":false,"type":{"name":"(e: HoverEvent) => void"}},"onHoverEnd":{"defaultValue":null,"description":"Handler that is called when a hover interaction ends.","name":"onHoverEnd","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"}],"required":false,"type":{"name":"(e: HoverEvent) => void"}},"onHoverChange":{"defaultValue":null,"description":"Handler that is called when the hover state changes.","name":"onHoverChange","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"}],"required":false,"type":{"name":"(isHovering: boolean) => void"}},"className":{"defaultValue":null,"description":"The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. A function may be provided to compute the class based on component state.","name":"className","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"StyleRenderProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"StyleRenderProps"}],"required":false,"type":{"name":"string | ((values: ButtonRenderProps) => string)"}},"name":{"defaultValue":null,"description":"Submitted as a pair with the button's value as part of the form data.","name":"name","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"ref":{"defaultValue":null,"description":"Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null`\n(or call the ref with `null` if you passed a callback ref).\n@see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs}","name":"ref","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@types+react@18.2.67/node_modules/@types/react/index.d.ts","name":"RefAttributes"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@types+react@18.2.67/node_modules/@types/react/index.d.ts","name":"RefAttributes"}],"required":false,"type":{"name":"LegacyRef<HTMLElement>"}},"key":{"defaultValue":null,"description":"","name":"key","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@types+react@18.2.67/node_modules/@types/react/index.d.ts","name":"Attributes"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@types+react@18.2.67/node_modules/@types/react/index.d.ts","name":"Attributes"}],"required":false,"type":{"name":"Key"}}},"groups":{"default":{"variant":{"defaultValue":{"value":"\"primary\""},"description":"The visual style of the button.\n*","name":"variant","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"\"primary\" | \"secondary\" | \"danger\" | \"upsell\" | \"ghost-primary\" | \"ghost-secondary\" | \"ghost-danger\""}},"size":{"defaultValue":{"value":"\"md\""},"description":"A button can vary in size.","name":"size","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"ResponsiveProp<\"sm\" | \"md\">"}},"fluid":{"defaultValue":null,"description":"Whether or not the button takes up the width of its container.","name":"fluid","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"ResponsiveProp<boolean>"}},"isLoading":{"defaultValue":null,"description":"","name":"isLoading","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"boolean"}},"href":{"defaultValue":null,"description":"A URL to link to. Setting this makes the component render an `a` tag instead of a `button`","name":"href","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"target":{"defaultValue":null,"description":"The target window for the link.","name":"target","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"rel":{"defaultValue":null,"description":"The relationship between the linked resource and the current page. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).","name":"rel","parent":{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/packages/components/src/buttons/src/Button.tsx","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"form":{"defaultValue":null,"description":"The <form> element to associate the button with.\nThe value of this attribute must be the id of a <form> in the same document.","name":"form","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"slot":{"defaultValue":null,"description":"A slot name for the component. Slots allow the component to receive props from a parent component.\nAn explicit `null` value indicates that the local props completely override all props received from a parent.","name":"slot","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"SlotProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"SlotProps"}],"required":false,"type":{"name":"string"}},"style":{"defaultValue":null,"description":"The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state.","name":"style","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"StyleRenderProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"StyleRenderProps"}],"required":false,"type":{"name":"CSSProperties | ((values: ButtonRenderProps) => CSSProperties)"}},"children":{"defaultValue":null,"description":"The children of the component. A function may be provided to alter the children based on component state.","name":"children","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"RenderProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"RenderProps"}],"required":false,"type":{"name":"ReactNode | ((values: ButtonRenderProps) => ReactNode)"}},"isDisabled":{"defaultValue":null,"description":"Whether the button is disabled.","name":"isDisabled","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"boolean"}},"autoFocus":{"defaultValue":null,"description":"Whether the element should receive focus on render.","name":"autoFocus","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusableProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusableProps"}],"required":false,"type":{"name":"boolean"}},"excludeFromTabOrder":{"defaultValue":null,"description":"Whether to exclude the element from the sequential tab order. If true,\nthe element will not be focusable via the keyboard by tabbing. This should\nbe avoided except in rare scenarios where an alternative means of accessing\nthe element or its functionality via the keyboard is available.","name":"excludeFromTabOrder","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"FocusableDOMProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"FocusableDOMProps"}],"required":false,"type":{"name":"boolean"}},"id":{"defaultValue":null,"description":"The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id).","name":"id","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"DOMProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"DOMProps"}],"required":false,"type":{"name":"string"}},"formAction":{"defaultValue":null,"description":"The URL that processes the information submitted by the button.\nOverrides the action attribute of the button's form owner.","name":"formAction","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"formEncType":{"defaultValue":null,"description":"Indicates how to encode the form data that is submitted.","name":"formEncType","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"formMethod":{"defaultValue":null,"description":"Indicates the HTTP method used to submit the form.","name":"formMethod","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"formNoValidate":{"defaultValue":null,"description":"Indicates that the form is not to be validated when it is submitted.","name":"formNoValidate","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"boolean"}},"formTarget":{"defaultValue":null,"description":"Overrides the target attribute of the button's form owner.","name":"formTarget","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"value":{"defaultValue":null,"description":"The value associated with the button's name when it's submitted with the form data.","name":"value","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"className":{"defaultValue":null,"description":"The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. A function may be provided to compute the class based on component state.","name":"className","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"StyleRenderProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"StyleRenderProps"}],"required":false,"type":{"name":"string | ((values: ButtonRenderProps) => string)"}},"name":{"defaultValue":null,"description":"Submitted as a pair with the button's value as part of the form data.","name":"name","parent":{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/react-aria-components@1.1.1_react-dom@18.2.0_react@18.2.0/node_modules/react-aria-components/dist/types.d.ts","name":"ButtonProps"}],"required":false,"type":{"name":"string"}},"ref":{"defaultValue":null,"description":"Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null`\n(or call the ref with `null` if you passed a callback ref).\n@see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs}","name":"ref","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@types+react@18.2.67/node_modules/@types/react/index.d.ts","name":"RefAttributes"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@types+react@18.2.67/node_modules/@types/react/index.d.ts","name":"RefAttributes"}],"required":false,"type":{"name":"LegacyRef<HTMLElement>"}},"key":{"defaultValue":null,"description":"","name":"key","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@types+react@18.2.67/node_modules/@types/react/index.d.ts","name":"Attributes"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@types+react@18.2.67/node_modules/@types/react/index.d.ts","name":"Attributes"}],"required":false,"type":{"name":"Key"}}},"events":{"onPress":{"defaultValue":null,"description":"Handler that is called when the press is released over the target.","name":"onPress","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"}],"required":false,"type":{"name":"(e: PressEvent) => void"}},"onPressStart":{"defaultValue":null,"description":"Handler that is called when a press interaction starts.","name":"onPressStart","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"}],"required":false,"type":{"name":"(e: PressEvent) => void"}},"onPressEnd":{"defaultValue":null,"description":"Handler that is called when a press interaction ends, either\nover the target or when the pointer leaves the target.","name":"onPressEnd","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"}],"required":false,"type":{"name":"(e: PressEvent) => void"}},"onPressChange":{"defaultValue":null,"description":"Handler that is called when the press state changes.","name":"onPressChange","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"}],"required":false,"type":{"name":"(isPressed: boolean) => void"}},"onPressUp":{"defaultValue":null,"description":"Handler that is called when a press is released over the target, regardless of\nwhether it started on the target or not.","name":"onPressUp","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"PressEvents"}],"required":false,"type":{"name":"(e: PressEvent) => void"}},"onFocus":{"defaultValue":null,"description":"Handler that is called when the element receives focus.","name":"onFocus","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"}],"required":false,"type":{"name":"(e: FocusEvent<Element, Element>) => void"}},"onBlur":{"defaultValue":null,"description":"Handler that is called when the element loses focus.","name":"onBlur","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"}],"required":false,"type":{"name":"(e: FocusEvent<Element, Element>) => void"}},"onFocusChange":{"defaultValue":null,"description":"Handler that is called when the element's focus status changes.","name":"onFocusChange","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"FocusEvents"}],"required":false,"type":{"name":"(isFocused: boolean) => void"}},"onKeyDown":{"defaultValue":null,"description":"Handler that is called when a key is pressed.","name":"onKeyDown","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"KeyboardEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"KeyboardEvents"}],"required":false,"type":{"name":"(e: KeyboardEvent) => void"}},"onKeyUp":{"defaultValue":null,"description":"Handler that is called when a key is released.","name":"onKeyUp","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"KeyboardEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"KeyboardEvents"}],"required":false,"type":{"name":"(e: KeyboardEvent) => void"}},"onHoverStart":{"defaultValue":null,"description":"Handler that is called when a hover interaction starts.","name":"onHoverStart","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"}],"required":false,"type":{"name":"(e: HoverEvent) => void"}},"onHoverEnd":{"defaultValue":null,"description":"Handler that is called when a hover interaction ends.","name":"onHoverEnd","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"}],"required":false,"type":{"name":"(e: HoverEvent) => void"}},"onHoverChange":{"defaultValue":null,"description":"Handler that is called when the hover state changes.","name":"onHoverChange","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/events.d.ts","name":"HoverEvents"}],"required":false,"type":{"name":"(isHovering: boolean) => void"}}},"a11y":{"aria-expanded":{"defaultValue":null,"description":"Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed.","name":"aria-expanded","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"}],"required":false,"type":{"name":"boolean | \"true\" | \"false\""}},"aria-haspopup":{"defaultValue":null,"description":"Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element.","name":"aria-haspopup","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"}],"required":false,"type":{"name":"boolean | \"dialog\" | \"menu\" | \"grid\" | \"true\" | \"false\" | \"listbox\" | \"tree\""}},"aria-controls":{"defaultValue":null,"description":"Identifies the element (or elements) whose contents or presence are controlled by the current element.","name":"aria-controls","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"}],"required":false,"type":{"name":"string"}},"aria-pressed":{"defaultValue":null,"description":"Indicates the current \"pressed\" state of toggle buttons.","name":"aria-pressed","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"}],"required":false,"type":{"name":"boolean | \"true\" | \"false\" | \"mixed\""}},"type":{"defaultValue":{"value":"'button'"},"description":"The behavior of the button when used in an HTML form.","name":"type","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+button@3.9.2_react@18.2.0/node_modules/@react-types/button/src/index.d.ts","name":"AriaBaseButtonProps"}],"required":false,"type":{"name":"\"button\" | \"submit\" | \"reset\""}},"aria-label":{"defaultValue":null,"description":"Defines a string value that labels the current element.","name":"aria-label","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"}],"required":false,"type":{"name":"string"}},"aria-labelledby":{"defaultValue":null,"description":"Identifies the element (or elements) that labels the current element.","name":"aria-labelledby","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"}],"required":false,"type":{"name":"string"}},"aria-describedby":{"defaultValue":null,"description":"Identifies the element (or elements) that describes the object.","name":"aria-describedby","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"}],"required":false,"type":{"name":"string"}},"aria-details":{"defaultValue":null,"description":"Identifies the element (or elements) that provide a detailed, extended description for the object.","name":"aria-details","parent":{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"},"declarations":[{"fileName":"wl-hopper/node_modules/.pnpm/@react-types+shared@3.22.1_react@18.2.0/node_modules/@react-types/shared/src/dom.d.ts","name":"AriaLabelingProps"}],"required":false,"type":{"name":"string"}}}}}] \ No newline at end of file diff --git a/apps/docs/package.json b/apps/docs/package.json index 3d2c96645..2de47632f 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -11,7 +11,8 @@ "lint": "next lint", "lint:types": "tsc --noEmit", "storybook": "storybook dev -p 6010", - "build:storybook": "storybook build" + "build:storybook": "storybook build", + "build:componentData": "tsx scripts/generateComponentData.mts" }, "dependencies": { "clsx": "2.1.0", @@ -48,6 +49,8 @@ "eslint": "8.57.0", "eslint-config-next": "14.1.4", "eslint-plugin-storybook": "0.8.0", + "react-docgen-typescript": "^2.2.2", + "shiki": "^1.3.0", "storybook": "8.0.2", "tsconfig-paths-webpack-plugin": "4.1.0", "typescript": "5.4.2" diff --git a/apps/docs/scripts/generateComponentData.mts b/apps/docs/scripts/generateComponentData.mts new file mode 100644 index 000000000..0339aff7d --- /dev/null +++ b/apps/docs/scripts/generateComponentData.mts @@ -0,0 +1,146 @@ +import fs from "fs"; +import path from "path"; +import docgenTs, {type ComponentDoc, PropItem} from "react-docgen-typescript"; + +interface ComponentData { + name: string; + filePath: string; +} + +interface Group { + [key: string]: PropItem; +} + +type Groups = { [key: string]: Group } + +type GroupsConfig = { + [key: string]: string; +} + +export interface ComponentDocWithGroups extends ComponentDoc { + groups: Groups; +} + +const PACKAGES = path.join(process.cwd(), "..", "..", "packages", "components", "src"); +const COMPONENT_DATA = path.join(process.cwd(), "datas", "components"); + +const tsConfigParser = docgenTs.withDefaultConfig({ + propFilter: (prop) => { + // Remove props from StyledSystemProps + return prop?.parent?.name !== "StyledSystemProps"; + } +}); + +async function writeFile(filename: string, data: ComponentDocWithGroups[]) { + if (!fs.existsSync(COMPONENT_DATA)) { + fs.mkdirSync(COMPONENT_DATA) + } + + fs.writeFile(`${COMPONENT_DATA}/${filename}.json`, JSON.stringify(data), function (err) { + if (err) { + console.error(err) + throw err; + } + console.log(`${filename} api is created!`) + }) +} + +function getComponentName(filePath: string) { + return path.basename(filePath, path.extname(filePath)); +} + +function getFormattedData(data: ComponentDoc[]): ComponentDocWithGroups[] { + // Define the groups and their corresponding terms + + const groupsConfig: GroupsConfig = { + events: "Events", + a11y: 'Aria', + // Add more groups here as needed + }; + + return data.map(component => { + // Initialize the groups + const groups: Groups = { + default: {}, + ...Object.keys(groupsConfig).reduce((acc, group) => ({...acc, [group]: {}}), {}), + }; + + Object.entries(component.props).forEach(([key, prop]) => { + let added = false; + + // Check each group to see if the prop should be added to it + Object.entries(groupsConfig).forEach(([group, term]) => { + if (prop.parent?.name.includes(term)) { + groups[group][key] = prop; + added = true; + } + }); + + // If the prop wasn't added to any group, add it to the default group + if (!added) { + groups.default[key] = prop; + } + }); + + return { + ...component, + groups + }; + }) +} + +async function generateComponentList(source: string): Promise<(ComponentData | undefined)[]> { + const subdirs = fs.readdirSync(source); + const files = await Promise.all(subdirs.map(async (subdir) => { + const res = path.resolve(source, subdir); + + // Checks if the path corresponds to a directory + if (fs.statSync(res).isDirectory()) { + return generateComponentList(res); + } + + // Checks whether the file is in the docs or tests directory + if (/\/(docs|tests)\/|index\.ts$/.test(res)) { + return; + } + + // Checks whether the file is a .ts or .tsx file + if (/\.tsx?$/.test(res)) { + const name = getComponentName(res); + return {name, filePath: res}; + } + })); + + return files.flat().filter(Boolean) as ComponentData[] +} + +async function generateComponentData() { + console.log('Start api generation for components'); + + // const components = await generateComponentList(PACKAGES); + // Data for the tests only + const components = [{ + name: "Button", + filePath: `${PACKAGES}/buttons/src/Button.tsx` + }] + + if (!components.length) { + console.error('No components found'); + return; + } + + for (const component of components) { + if (component) { + + const data = tsConfigParser.parse(component.filePath); + const {name} = component; + const formattedData = getFormattedData(data); + + await writeFile(name, formattedData); + } + } + + return; +} + +generateComponentData().then(() => console.log('🎉 Success')).catch(err => console.error(err)); diff --git a/packages/components/src/buttons/src/Button.tsx b/packages/components/src/buttons/src/Button.tsx index 356cfcfcd..4329e1a0f 100644 --- a/packages/components/src/buttons/src/Button.tsx +++ b/packages/components/src/buttons/src/Button.tsx @@ -1,15 +1,33 @@ import { IconContext } from "@hopper-ui/icons"; -import { type StyledComponentProps, useStyledSystem, type ResponsiveProp, useResponsiveValue } from "@hopper-ui/styled-system"; +import { + type StyledComponentProps, + useStyledSystem, + type ResponsiveProp, + useResponsiveValue +} from "@hopper-ui/styled-system"; import { useRouter, shouldClientNavigate, filterDOMProps, chain } from "@react-aria/utils"; import { type ForwardedRef, forwardRef, type MouseEvent, type MutableRefObject } from "react"; import { useButton, useHover, useFocusRing, mergeProps } from "react-aria"; -import { useContextProps, composeRenderProps, type ButtonProps as RACButtonProps, type ButtonRenderProps, ButtonContext as RACButtonContext } from "react-aria-components"; +import { + useContextProps, + composeRenderProps, + type ButtonProps as RACButtonProps, + type ButtonRenderProps, + ButtonContext as RACButtonContext +} from "react-aria-components"; import { IconListContext } from "../../IconList/index.ts"; import { useLocalizedString } from "../../intl/index.ts"; import { Spinner } from "../../Spinner/index.ts"; import { TextContext, Text } from "../../Text/index.ts"; -import { composeClassnameRenderProps, SlotProvider, cssModule, useSlot, isTextOnlyChildren, useRenderProps } from "../../utils/index.ts"; +import { + composeClassnameRenderProps, + SlotProvider, + cssModule, + useSlot, + isTextOnlyChildren, + useRenderProps +} from "../../utils/index.ts"; import { ButtonContext, type ButtonContextValue } from "./ButtonContext.ts"; @@ -72,7 +90,13 @@ function useSimulatedRACButton(props: ButtonProps, ref: MutableRefObject<HTMLEle isDisabled: props.isDisabled || props.isLoading }, ref); - const state: ButtonRenderProps = { isFocused, isFocusVisible, isHovered, isPressed, isDisabled: props.isDisabled || false }; + const state: ButtonRenderProps = { + isFocused, + isFocusVisible, + isHovered, + isPressed, + isDisabled: props.isDisabled || false + }; const mergedProps = { ...mergeProps(buttonProps, focusProps, hoverProps), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bde84e5a5..870b8dea7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -231,6 +231,12 @@ importers: eslint-plugin-storybook: specifier: 0.8.0 version: 0.8.0(eslint@8.57.0)(typescript@5.4.2) + react-docgen-typescript: + specifier: ^2.2.2 + version: 2.2.2(typescript@5.4.2) + shiki: + specifier: ^1.3.0 + version: 1.3.0 storybook: specifier: 8.0.2 version: 8.0.2(react-dom@18.2.0)(react@18.2.0) @@ -245,7 +251,7 @@ importers: dependencies: '@hopper-ui/icons': specifier: '*' - version: 1.10.0(@hopper-ui/styled-system@packages+styled-system)(react-aria-components@1.1.1)(react-dom@18.2.0)(react@18.2.0) + version: link:../icons '@react-aria/utils': specifier: ^3.23.2 version: 3.23.2(react@18.2.0) @@ -2426,10 +2432,6 @@ packages: peerDependencies: '@effect-ts/otel-node': '*' peerDependenciesMeta: - '@effect-ts/core': - optional: true - '@effect-ts/otel': - optional: true '@effect-ts/otel-node': optional: true dependencies: @@ -3078,22 +3080,6 @@ packages: '@hapi/hoek': 9.3.0 dev: true - /@hopper-ui/icons@1.10.0(@hopper-ui/styled-system@packages+styled-system)(react-aria-components@1.1.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-B4XCmADS1Wc4DiZTTs2PzD4tXl5kgyCwc4Urw9dcOcxO48Twf97whVlj0VGprQ90uKXEC1pCk0Kcgi1jA0ekkA==} - peerDependencies: - '@hopper-ui/styled-system': ^1 - react: ^18 - react-aria-components: ^1.1.0 - react-dom: ^18 - dependencies: - '@hopper-ui/styled-system': link:packages/styled-system - '@react-aria/utils': 3.23.2(react@18.2.0) - clsx: 2.1.0 - react: 18.2.0 - react-aria-components: 1.1.1(react-dom@18.2.0)(react@18.2.0) - react-dom: 18.2.0(react@18.2.0) - dev: false - /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -5803,7 +5789,6 @@ packages: /@shikijs/core@1.3.0: resolution: {integrity: sha512-7fedsBfuILDTBmrYZNFI8B6ATTxhQAasUHllHmjvSZPnoq4bULWoTpHwmuQvZ8Aq03/tAa2IGo6RXqWtHdWaCA==} - dev: false /@sideway/address@4.1.5: resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} @@ -18731,7 +18716,6 @@ packages: resolution: {integrity: sha512-9aNdQy/etMXctnPzsje1h1XIGm9YfRcSksKOGqZWXA/qP9G18/8fpz5Bjpma8bOgz3tqIpjERAd6/lLjFyzoww==} dependencies: '@shikijs/core': 1.3.0 - dev: false /side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}