Skip to content

Commit

Permalink
feat: add Resizable and Avatar components
Browse files Browse the repository at this point in the history
  • Loading branch information
samlaycock committed Dec 28, 2023
1 parent ee40b71 commit 9cc6d0b
Show file tree
Hide file tree
Showing 19 changed files with 399 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"prism-react-renderer": "~2.3.1",
"prismjs": "~1.29.0",
"react-aria-components": "~1.0.0",
"react-resizable-panels": "~1.0.5",
"sonner": "~1.2.4",
"tailwind-merge": "~2.2.0"
},
Expand Down
77 changes: 77 additions & 0 deletions packages/react/src/components/avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { type VariantProps, cva } from "class-variance-authority";

import { cn } from "../utils";

const avatarVariants = cva(
"rounded-full bg-neutral-200 dark:bg-neutral-800 overflow-hidden",
{
variants: {
size: {
default: "h-8 w-8",
sm: "h-6 w-6",
lg: "h-10 w-10",
xl: "h-12 w-12",
},
},
defaultVariants: {
size: "default",
},
},
);

export interface AvatarProps extends VariantProps<typeof avatarVariants> {
alt?: string;
className?: string;
indicator?: "neutral" | "success" | "error";
src?: string;
}

function Avatar({
alt,
className,
indicator,
size,
src,
...props
}: AvatarProps) {
let indicatorClassName =
"absolute right-0 bottom-0 block h-2 w-2 rounded-full ring-2 ring-neutral-50 dark:ring-neutral-900";

if (indicator === "neutral") {
indicatorClassName = `${indicatorClassName} bg-neutral-300 dark:bg-neutral-600`;
} else if (indicator === "success") {
indicatorClassName = `${indicatorClassName} bg-success-500`;
} else if (indicator === "error") {
indicatorClassName = `${indicatorClassName} bg-danger-500`;
}

return (
<span className="relative inline-block" title={alt} {...props}>
<img
className={cn(avatarVariants({ size, className }))}
src={src}
alt={alt}
/>
{indicator && <span className={indicatorClassName} />}
</span>
);
}

Avatar.displayName = "Avatar";

export interface AvatarGroupProps extends React.PropsWithChildren {
className?: string;
}

Avatar.Group = function AvatarGroup({ children, className }: AvatarGroupProps) {
return (
<div className={cn("flex -space-x-1 overflow-hidden", className)}>
{children}
</div>
);
};

// @ts-expect-error
Avatar.Group.displayName = "Avatar.Group";

export { Avatar };
1 change: 1 addition & 0 deletions packages/react/src/components/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Button as AriaButton,
type ButtonProps as BaseButtonProps,
} from "react-aria-components";

import { cn } from "../utils";

const buttonVariants = cva(
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/callout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type VariantProps, cva } from "class-variance-authority";

import { cn } from "../utils";

const calloutVariants = cva("", {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
type CheckboxGroupProps as BaseCheckboxGroupProps,
type CheckboxProps as BaseCheckboxProps,
} from "react-aria-components";

import { cn } from "../utils";

export interface CheckboxProps extends Omit<BaseCheckboxProps, "children"> {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
} from "@heroicons/react/24/solid";
import { Highlight } from "prism-react-renderer";
import { Fragment, useState } from "react";

import { cn } from "../utils";

export interface CodeProps {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/logo.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type VariantProps, cva } from "class-variance-authority";
import { Text } from "react-aria-components";

import { cn } from "../utils";

const logoVariants = cva("font-display font-black uppercase leading-none", {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/number-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
type NumberFieldProps as BaseNumberFieldProps,
Text,
} from "react-aria-components";

import { cn } from "../utils";

export interface NumberFieldProps extends BaseNumberFieldProps {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/prose.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type VariantProps, cva } from "class-variance-authority";

import { cn } from "../utils";

const proseVariants = cva("prose dark:prose-invert", {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
type RadioGroupProps as BaseRadioGroupProps,
type RadioProps as BaseRadioProps,
} from "react-aria-components";

import { cn } from "../utils";

export interface RadioProps extends Omit<BaseRadioProps, "children"> {
Expand Down
83 changes: 83 additions & 0 deletions packages/react/src/components/resizable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
Panel,
PanelGroup,
type PanelGroupProps,
type PanelProps,
PanelResizeHandle,
type PanelResizeHandleProps,
} from "react-resizable-panels";

import { cn } from "../utils";

export type ResizableProps = PanelGroupProps;

function Resizable({ className, ...props }: ResizableProps) {
return (
<PanelGroup
className={cn(
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
className,
)}
{...props}
/>
);
}

Resizable.displayName = "Resizable";

export type ResizablePanelProps = PanelProps;

Resizable.Panel = function ResizablePanel(props: ResizablePanelProps) {
return <Panel {...props} />;
};

// @ts-expect-error
Resizable.Panel.displayName = "Resizable.Panel";

export interface ResizableHandleProps extends PanelResizeHandleProps {
withHandle?: boolean;
}

Resizable.Handle = function ResizableHandle({
className,
withHandle,
...props
}: ResizableHandleProps) {
return (
<PanelResizeHandle
className={cn(
"relative flex w-px items-center justify-center bg-neutral-100 after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 hover:bg-brand-500 data-[resize-handle-active=pointer]:outline-none data-[resize-handle-active=pointer]:bg-brand-500 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90 dark:bg-neutral-900",
className,
)}
{...props}
>
{withHandle && (
<div className="grid grid-cols-2 h-4 w-3 rounded-sm bg-neutral-200 border-2 border-neutral-100 z-10 dark:bg-neutral-800 dark:border-neutral-900">
<div className="flex justify-center items-center">
<span className="rounded-full h-[2px] w-[2px] bg-neutral-300 dark:bg-neutral-700" />
</div>
<div className="flex justify-center items-center">
<span className="rounded-full h-[2px] w-[2px] bg-neutral-300 dark:bg-neutral-700" />
</div>
<div className="flex justify-center items-center">
<span className="rounded-full h-[2px] w-[2px] bg-neutral-300 dark:bg-neutral-700" />
</div>
<div className="flex justify-center items-center">
<span className="rounded-full h-[2px] w-[2px] bg-neutral-300 dark:bg-neutral-700" />
</div>
<div className="flex justify-center items-center">
<span className="rounded-full h-[2px] w-[2px] bg-neutral-300 dark:bg-neutral-700" />
</div>
<div className="flex justify-center items-center">
<span className="rounded-full h-[2px] w-[2px] bg-neutral-300 dark:bg-neutral-700" />
</div>
</div>
)}
</PanelResizeHandle>
);
};

// @ts-expect-error
Resizable.Handle.displayName = "Resizable.Handle";

export { Resizable };
1 change: 1 addition & 0 deletions packages/react/src/components/table-of-contents.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { slugifyWithCounter } from "@sindresorhus/slugify";
import { useCallback, useEffect, useState } from "react";

import { cn } from "../utils";

interface TOCNode {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Tabs as BaseTabs,
type TabsProps as BaseTabsProps,
} from "react-aria-components";

import { cn } from "../utils";

export type TabsProps = BaseTabsProps;
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/text-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
TextField as BaseTextField,
type TextFieldProps as BaseTextFieldProps,
} from "react-aria-components";

import { cn } from "../utils";

export interface TextFieldProps extends BaseTextFieldProps {
Expand Down
18 changes: 18 additions & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import {
Avatar,
type AvatarGroupProps,
type AvatarProps,
} from "./components/avatar";
import { Button, type ButtonProps } from "./components/button";
import { Callout, type CalloutProps } from "./components/callout";
import {
Expand Down Expand Up @@ -28,6 +33,12 @@ import {
type RadioGroupProps,
type RadioProps,
} from "./components/radio";
import {
Resizable,
type ResizableHandleProps,
type ResizablePanelProps,
type ResizableProps,
} from "./components/resizable";
import { Slider, type SliderProps } from "./components/slider";
import {
TableOfContents,
Expand All @@ -48,6 +59,7 @@ import {
import { Toaster, useToast } from "./components/toasts";

export {
Avatar,
Button,
Callout,
Checkbox,
Expand All @@ -59,13 +71,16 @@ export {
Progress,
Prose,
Radio,
Resizable,
Slider,
TableOfContents,
Tabs,
TextField,
ThemeSelector,
Toaster,
useToast,
type AvatarGroupProps,
type AvatarProps,
type ButtonProps,
type CalloutProps,
type CheckboxGroupProps,
Expand All @@ -84,6 +99,9 @@ export {
type ProseProps,
type RadioGroupProps,
type RadioProps,
type ResizableHandleProps,
type ResizablePanelProps,
type ResizableProps,
type SliderProps,
type TableOfContentsProps,
type TabsButtonProps,
Expand Down
13 changes: 13 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 9cc6d0b

Please sign in to comment.