From 4dd4889d2214f957a18b91e79a424bbc0b7cd529 Mon Sep 17 00:00:00 2001 From: Robb Niznik Date: Fri, 1 Nov 2024 17:02:35 -0400 Subject: [PATCH] feat(components): add `GridList` --- packages/components/src/GridList.tsx | 63 +++++++++++++++++++ packages/components/src/index.ts | 2 + .../components/src/styles/GridList.module.css | 17 +++++ .../components/stories/GridList.stories.tsx | 32 ++++++++++ .../components/stories/ListBox.stories.tsx | 1 - 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 packages/components/src/GridList.tsx create mode 100644 packages/components/src/styles/GridList.module.css create mode 100644 packages/components/stories/GridList.stories.tsx diff --git a/packages/components/src/GridList.tsx b/packages/components/src/GridList.tsx new file mode 100644 index 000000000..d75ee2255 --- /dev/null +++ b/packages/components/src/GridList.tsx @@ -0,0 +1,63 @@ +import type { forwardRefType } from '@react-types/shared'; +import type { ForwardedRef } from 'react'; +import type { GridListItemProps, GridListProps } from 'react-aria-components'; + +import { cva } from 'class-variance-authority'; +import { forwardRef } from 'react'; +import { + GridList as AriaGridList, + GridListItem as AriaGridListItem, + composeRenderProps, +} from 'react-aria-components'; + +import styles from './styles/GridList.module.css'; + +const list = cva(styles.list); +const item = cva(styles.item); + +const _GridList = ( + props: GridListProps, + ref: ForwardedRef, +) => { + return ( + + list({ ...renderProps, className }), + )} + /> + ); +}; + +/** + * A grid list displays a list of interactive items, with support for keyboard navigation, single or multiple selection, and row actions. + * + * https://react-spectrum.adobe.com/react-aria/GridList.html + */ +const GridList = (forwardRef as forwardRefType)(_GridList); + +const _GridListItem = (props: GridListItemProps, ref: ForwardedRef) => { + const textValue = + props.textValue || (typeof props.children === 'string' ? props.children : undefined); + return ( + + item({ ...renderProps, className }), + )} + /> + ); +}; + +/** + * A GridListItem represents an individual item in a GridList. + * + * https://react-spectrum.adobe.com/react-aria/GridList.html + */ +const GridListItem = (forwardRef as forwardRefType)(_GridListItem); + +export { GridList, GridListItem }; +export type { GridListProps, GridListItemProps }; diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 165234d97..a3d1a2012 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -28,6 +28,7 @@ export type { FieldErrorProps } from './FieldError'; export type { FieldGroupProps } from './FieldGroup'; export type { FileTriggerProps } from './FileTrigger'; export type { FormProps } from './Form'; +export type { GridListProps, GridListItemProps } from './GridList'; export type { GroupProps } from './Group'; export type { HeadingProps } from './Heading'; export type { InputProps } from './Input'; @@ -102,6 +103,7 @@ export { FieldError } from './FieldError'; export { FieldGroup } from './FieldGroup'; export { FileTrigger } from './FileTrigger'; export { Form } from './Form'; +export { GridList, GridListItem } from './GridList'; export { Group } from './Group'; export { Header } from './Header'; export { Heading } from './Heading'; diff --git a/packages/components/src/styles/GridList.module.css b/packages/components/src/styles/GridList.module.css new file mode 100644 index 000000000..131d680c2 --- /dev/null +++ b/packages/components/src/styles/GridList.module.css @@ -0,0 +1,17 @@ +.list { + display: flex; + flex-direction: column; +} + +.item { + composes: interactive from './base.module.css'; + display: flex; + align-items: center; + box-shadow: inset 0 -1px 0 0 var(--lp-color-border-ui-secondary); + outline: none; + padding-block: var(--lp-spacing-300); + + &[data-hovered] { + background-color: var(--lp-color-bg-ui-secondary); + } +} diff --git a/packages/components/stories/GridList.stories.tsx b/packages/components/stories/GridList.stories.tsx new file mode 100644 index 000000000..d39ac9a4e --- /dev/null +++ b/packages/components/stories/GridList.stories.tsx @@ -0,0 +1,32 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { GridList, GridListItem } from '../src'; + +const meta: Meta = { + component: GridList, + // @ts-ignore + subcomponents: { GridListItem }, + title: 'Components/Collections/GridList', + parameters: { + status: { + type: import.meta.env.STORYBOOK_PACKAGE_STATUS__COMPONENTS, + }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Example: Story = { + args: { + children: ( + <> + Item one + Item two + Item three + + ), + selectionMode: 'single', + }, +}; diff --git a/packages/components/stories/ListBox.stories.tsx b/packages/components/stories/ListBox.stories.tsx index 66c2ece68..223620a65 100644 --- a/packages/components/stories/ListBox.stories.tsx +++ b/packages/components/stories/ListBox.stories.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react-hooks/rules-of-hooks */ import type { Meta, StoryObj } from '@storybook/react'; import type { Selection as AriaSelection } from 'react-aria-components';