Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): Implements a sheet component #17

Merged
merged 4 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions apps/website/docs/apis/sheet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
sidebar_position: 10
title: <Sheet />
---

# Sheet API

<AvailableFrom version="0.3.0" />

API reference docs for the React Sheet component.
Learn about the props of this exported module.

## Demos

:::tip

For examples and details on the usage of this React component, visit the component demo pages:

- [Sheet](../components/sheet)

:::

## Import

```jsx
import { Sheet } from 'tailwind-joy/components';
```

## Props

:::info

The `ref` is forwarded to the root element.

:::

By default, props available for HTML `<div>` are also available for RadioGroup component.
Other props are as follows:

### `color`

The color of the component.

- Type: `'primary' | 'neutral' | 'danger' | 'success' | 'warning'`
- Default: `'neutral'`

### `variant`

The variant of the component.

- Type: `'solid' | 'soft' | 'outlined' | 'plain'`
- Default: `'plain'`
4 changes: 4 additions & 0 deletions apps/website/docs/components/surfaces/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"label": "Surfaces",
"position": 4
}
46 changes: 46 additions & 0 deletions apps/website/docs/components/surfaces/sheet.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
sidebar_position: 3
slug: /components/sheet
---

# Sheet

<AvailableFrom version="0.3.0" />

Sheet is a generic container that supports Tailwind-Joy's variants.

## Basics

The `Sheet` component, in addition to the variants, also has access to the `color` prop, allowing you to use every palette of the theme.

export function SheetBasics() {
return (
<DisplayStand>
<Sheet variant="outlined" color="neutral" className="p-8">
Hello world!
</Sheet>
</DisplayStand>
);
}

<SheetBasics />

```jsx
import { Sheet } from 'tailwind-joy/components';

export function SheetBasics() {
return (
<div>
<Sheet variant="outlined" color="neutral" className="p-8">
Hello world!
</Sheet>
</div>
);
}
```

## API

See the documentation below for a complete reference to all of the props available to the components mentioned here.

- [`<Sheet />`](../apis/sheet)
2 changes: 2 additions & 0 deletions apps/website/src/theme/MDXComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
LinearProgress,
Radio,
RadioGroup,
Sheet,
} from 'tailwind-joy/components';
import { AvailableFrom } from '@site/src/components/docs/AvailableFrom';
import { DeprecatedIn } from '@site/src/components/docs/DeprecatedIn';
Expand All @@ -35,6 +36,7 @@ export default {
LinearProgress,
Radio,
RadioGroup,
Sheet,

// --------------------------------

Expand Down
1 change: 1 addition & 0 deletions packages/tailwind-joy/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { IconButton } from './components/IconButton';
export { LinearProgress } from './components/LinearProgress';
export { Radio } from './components/Radio';
export { RadioGroup } from './components/RadioGroup';
export { Sheet } from './components/Sheet';
112 changes: 112 additions & 0 deletions packages/tailwind-joy/src/components/Sheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { clsx } from 'clsx';
import type { ComponentProps } from 'react';
import { forwardRef, useMemo } from 'react';
import { twMerge } from 'tailwind-merge';
import type { BaseVariants, GeneratorInput } from '@/base/types';
import { toVariableClass } from '../base/modifier';
import { baseTokens, colorTokens } from '../base/tokens';

function sheetRootVariants(
props?: Pick<BaseVariants, 'color' | 'variant'> & {
childRadius?: boolean;
},
) {
const { color = 'neutral', variant = 'plain', childRadius } = props ?? {};
const resolvedBgToken =
baseTokens[color][`${variant}Bg`] || baseTokens.background.surface;

return twMerge(
clsx([
'tj-sheet-root group/tj-sheet',
color !== 'neutral' || variant === 'solid'
? '[--Icon-color:currentColor]'
: toVariableClass(baseTokens.text.icon, 'Icon-color'),
toVariableClass(resolvedBgToken, 'ListItem-stickyBackground'),
toVariableClass(resolvedBgToken, 'Sheet-background'),
childRadius && [
'[--List-radius:calc(var(--tj-Sheet-childRadius)-var(--variant-borderWidth,0px))]',
'[--unstable_actionRadius:calc(var(--tj-Sheet-childRadius)-var(--variant-borderWidth,0px))]',
],
colorTokens.background.surface,
'relative',
'text-[1rem]',
'leading-normal',
colorTokens.text.secondary,
[
variant === 'outlined'
? '[--variant-borderWidth:1px] [border-width:var(--variant-borderWidth)] border-solid'
: '[--variant-borderWidth:0px]',
colorTokens[color][`${variant}Color`],
colorTokens[color][`${variant}Bg`],
colorTokens[color][`${variant}Border`],
],
]),
);
}

interface SheetRootVariants extends Pick<BaseVariants, 'color' | 'variant'> {}

type SheetRootProps = Omit<ComponentProps<'div'>, keyof SheetRootVariants> &
SheetRootVariants;

export const Sheet = forwardRef<HTMLDivElement, SheetRootProps>(
function SheetRoot(
{ children, className, style, color, variant, ...otherProps },
ref,
) {
const resolvedClassNames = twMerge(className).split(' ');
const resolvedBorderRadiusWithArbitraryValue = useMemo(() => {
const regExp = /^rounded-\[([^\]]+)\]$/;

return resolvedClassNames
.filter((text) => regExp.test(text))
.at(0)
?.replace(regExp, '$1');
}, [resolvedClassNames]);
const resolvedBorderRadiusWithArbitraryProperty = useMemo(() => {
const regExp = /^\[border-radius:([^\]]+)\]$/;

return resolvedClassNames
.filter((text) => regExp.test(text))
.at(0)
?.replace(regExp, '$1');
}, [resolvedClassNames]);

const instanceChildRadius =
resolvedBorderRadiusWithArbitraryProperty ||
resolvedBorderRadiusWithArbitraryValue;

return (
<div
ref={ref}
className={twMerge(
sheetRootVariants({
color,
variant,
childRadius: instanceChildRadius !== undefined,
}),
className,
)}
{...otherProps}
style={{
...style,
// @ts-expect-error
'--tj-Sheet-childRadius': instanceChildRadius,
}}
>
{children}
</div>
);
},
);

export const generatorInputs: GeneratorInput[] = [
{
generatorFn: sheetRootVariants,
variants: {
color: ['primary', 'neutral', 'danger', 'success', 'warning'],
variant: ['solid', 'soft', 'outlined', 'plain'],
childRadius: [false, true],
},
},
];
2 changes: 2 additions & 0 deletions packages/tailwind-joy/src/plugins/safelist-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { generatorInputs as iconButtonClassNameGeneratorInputs } from '../compon
import { generatorInputs as linearProgressClassNameGeneratorInputs } from '../components/LinearProgress';
import { generatorInputs as radioClassNameGeneratorInputs } from '../components/Radio';
import { generatorInputs as radioGroupClassNameGeneratorInputs } from '../components/RadioGroup';
import { generatorInputs as sheetClassNameGeneratorInputs } from '../components/Sheet';
import { generatorInputs as adaptedIconClassNameGeneratorInputs } from '../components/internal/class-adapter';

const SPACE = ' ';
Expand All @@ -23,6 +24,7 @@ const inputs: GeneratorInput[] = [
...linearProgressClassNameGeneratorInputs,
...radioClassNameGeneratorInputs,
...radioGroupClassNameGeneratorInputs,
...sheetClassNameGeneratorInputs,
];

function generate() {
Expand Down