Skip to content
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
2 changes: 2 additions & 0 deletions src/frontend/packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-toast": "^1.2.4",
"@radix-ui/react-toggle": "^1.1.1",
"@radix-ui/react-toggle-group": "^1.1.1",
"@tanstack/react-table": "^8.20.6",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
Expand Down
2 changes: 2 additions & 0 deletions src/frontend/packages/ui/src/components/Toggle/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Toggle } from './toggle';
export { ToggleGroup, ToggleGroupItem } from './toggle-group';
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { Meta, StoryObj } from '@storybook/react';
import { ToggleGroup, ToggleGroupItem } from './toggle-group';
import { useState } from 'react';
import { Mic, MicOff, ScreenShare, ScreenShareOff, Video, VideoOff } from 'lucide-react';

const meta: Meta<typeof ToggleGroup> = {
title: 'Widget/ToggleGroup',
component: ToggleGroup,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof ToggleGroup>;

export const Default: Story = {
render: () => {
const [isMicOn, setIsMicOn] = useState(false);
const [isVideoOn, setIsVideoOn] = useState(false);
const [isScreenSharing, setIsScreenSharing] = useState(false);

return (
<ToggleGroup
variant="outline"
type="multiple"
size="lg"
>
<ToggleGroupItem
value="mic"
onClick={() => setIsMicOn((prev) => !prev)}
>
{isMicOn ? <Mic /> : <MicOff />}
</ToggleGroupItem>
<ToggleGroupItem
value="video"
onClick={() => setIsVideoOn((prev) => !prev)}
>
{isVideoOn ? <Video /> : <VideoOff />}
</ToggleGroupItem>
<ToggleGroupItem
value="sharing"
onClick={() => setIsScreenSharing((prev) => !prev)}
>
{isScreenSharing ? <ScreenShare /> : <ScreenShareOff />}
</ToggleGroupItem>
</ToggleGroup>
);
},
};
55 changes: 55 additions & 0 deletions src/frontend/packages/ui/src/components/Toggle/toggle-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use client';

import * as React from 'react';
import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group';
import { type VariantProps } from 'class-variance-authority';

import { cn } from '@workspace/ui/lib/utils';
import { toggleVariants } from './toggle';

const ToggleGroupContext = React.createContext<VariantProps<typeof toggleVariants>>({
size: 'default',
variant: 'outline',
});

const ToggleGroup = React.forwardRef<
React.ElementRef<typeof ToggleGroupPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> & VariantProps<typeof toggleVariants>
>(({ className, variant, size, children, ...props }, ref) => (
<ToggleGroupPrimitive.Root
ref={ref}
className={cn('flex items-center justify-center gap-1', className)}
{...props}
>
<ToggleGroupContext.Provider value={{ variant, size }}>{children}</ToggleGroupContext.Provider>
</ToggleGroupPrimitive.Root>
));

ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;

const ToggleGroupItem = React.forwardRef<
React.ElementRef<typeof ToggleGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> & VariantProps<typeof toggleVariants>
>(({ className, children, variant, size, ...props }, ref) => {
const context = React.useContext(ToggleGroupContext);

return (
<ToggleGroupPrimitive.Item
ref={ref}
className={cn(
toggleVariants({
variant: context.variant || variant,
size: context.size || size,
}),
className,
)}
{...props}
>
{children}
</ToggleGroupPrimitive.Item>
);
});

ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;

export { ToggleGroup, ToggleGroupItem };
44 changes: 44 additions & 0 deletions src/frontend/packages/ui/src/components/Toggle/toggle.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Toggle } from './toggle';
import { Mic, MicOff } from 'lucide-react';
import { useState } from 'react';

const meta: Meta<typeof Toggle> = {
title: 'Widget/Toggle',
component: Toggle,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['default', 'outline'],
},
size: {
control: 'radio',
options: ['default', 'lg'],
},
},
};
export default meta;
type Story = StoryObj<typeof Toggle>;

export const Default: Story = {
args: {
variant: 'outline',
size: 'default',
},
render: (args) => {
const [isPressed, setIsPressed] = useState(false);
return (
<Toggle
{...args}
pressed={isPressed}
onPressedChange={setIsPressed}
>
{isPressed ? <Mic /> : <MicOff />}
</Toggle>
);
},
};
40 changes: 40 additions & 0 deletions src/frontend/packages/ui/src/components/Toggle/toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use client';

import * as React from 'react';
import * as TogglePrimitive from '@radix-ui/react-toggle';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@workspace/ui/lib/utils';

const toggleVariants = cva(
'inline-flex items-center justify-center rounded-full text-sm font-medium text-muted-foreground ring-offset-background transition-colors hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2',
{
variants: {
variant: {
default: 'bg-transparent',
outline: 'border border-input bg-transparent hover:text-accent-foreground',
},
size: {
default: 'w-10 h-10 px-3',
lg: 'w-12 h-12 px-5',
},
},
defaultVariants: {
variant: 'outline',
size: 'default',
},
},
);

const Toggle = React.forwardRef<React.ElementRef<typeof TogglePrimitive.Root>, React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>>(
({ className, variant, size, ...props }, ref) => (
<TogglePrimitive.Root
ref={ref}
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>
),
);

Toggle.displayName = TogglePrimitive.Root.displayName;

export { Toggle, toggleVariants };
1 change: 1 addition & 0 deletions src/frontend/packages/ui/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from './Toast';
export * from './Resizable';
export * from './Avatar';
export * from './Separate';
export * from './Toggle';
export * from './Popover';
export * from './Label';
export * from './Input';
103 changes: 103 additions & 0 deletions src/frontend/pnpm-lock.yaml

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

Loading