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
12 changes: 7 additions & 5 deletions packages/lsd-docs/app/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { Button, SidebarTrigger } from '@nipsys/lsd';
import { Button, SidebarTrigger, useSidebar } from '@nipsys/lsd';
import { GithubLogoIcon, MagnifyingGlassIcon } from '@phosphor-icons/react';
import Link from 'next/link';
import { FontToggle } from './docs/FontToggle';
Expand All @@ -12,12 +12,14 @@ interface HeaderProps {
}

export default function Header({ className }: HeaderProps) {
const { isMobile } = useSidebar();

return (
<header className={`w-full border-b ${className || ''}`}>
<div className="flex items-center justify-between py-(--lsd-spacing-small) px-(--lsd-spacing-base)">
<div className="flex items-center gap-(--lsd-spacing-base)">
<SidebarTrigger />
</div>
<div
className={`flex items-center ${isMobile ? 'justify-between' : 'justify-end'} py-(--lsd-spacing-small) px-(--lsd-spacing-base)`}
>
{isMobile && <SidebarTrigger />}

<div className="flex items-center gap-(--lsd-spacing-larger)">
<Button
Expand Down
141 changes: 141 additions & 0 deletions packages/lsd-docs/app/components/sidebar/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,74 @@ export default function MyComponent() {
</Typography>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>showTrigger</CardTitle>
<CardDescription>
Whether to show the sidebar trigger button. When true, displays the trigger
button to toggle sidebar visibility.
</CardDescription>
</CardHeader>
<CardContent>
<Typography variant="body2" className="block mb-(--lsd-spacing-smaller)">
<strong>Type:</strong> <code>boolean</code>
</Typography>
<Typography variant="label1" className="block mt-(--lsd-spacing-smaller)">
<strong>Optional</strong>
</Typography>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>triggerIconExpanded</CardTitle>
<CardDescription>
Icon element for the sidebar trigger when expanded. Passed to SidebarTrigger's
icon prop when sidebar is expanded.
</CardDescription>
</CardHeader>
<CardContent>
<Typography variant="body2" className="block mb-(--lsd-spacing-smaller)">
<strong>Type:</strong> <code>React.ReactNode</code>
</Typography>
<Typography variant="label1" className="block mt-(--lsd-spacing-smaller)">
<strong>Optional</strong>
</Typography>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>triggerIconCollapsed</CardTitle>
<CardDescription>
Icon element for the sidebar trigger when collapsed. Passed to SidebarTrigger's
icon prop when sidebar is collapsed.
</CardDescription>
</CardHeader>
<CardContent>
<Typography variant="body2" className="block mb-(--lsd-spacing-smaller)">
<strong>Type:</strong> <code>React.ReactNode</code>
</Typography>
<Typography variant="label1" className="block mt-(--lsd-spacing-smaller)">
<strong>Optional</strong>
</Typography>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>triggerClassName</CardTitle>
<CardDescription>
Class name for the sidebar trigger button. Applied to the SidebarTrigger
component.
</CardDescription>
</CardHeader>
<CardContent>
<Typography variant="body2" className="block mb-(--lsd-spacing-smaller)">
<strong>Type:</strong> <code>string</code>
</Typography>
<Typography variant="label1" className="block mt-(--lsd-spacing-smaller)">
<strong>Optional</strong>
</Typography>
</CardContent>
</Card>
</div>
</div>

Expand Down Expand Up @@ -468,6 +536,79 @@ export default function MyComponent() {
</Card>
</div>
</div>

<div className="mt-(--lsd-spacing-large)">
<Typography variant="h4" className="mb-(--lsd-spacing-base)">
SidebarTrigger
</Typography>
<div className="grid grid-cols-1 md:grid-cols-2 gap-(--lsd-spacing-base)">
<Card>
<CardHeader>
<CardTitle>icon</CardTitle>
<CardDescription>
Icon element to display in the trigger button. Defaults to SidebarSimpleIcon
with duotone weight.
</CardDescription>
</CardHeader>
<CardContent>
<Typography variant="body2" className="block mb-(--lsd-spacing-smaller)">
<strong>Type:</strong> <code>React.ReactNode</code>
</Typography>
<Typography variant="label1" className="block mt-(--lsd-spacing-smaller)">
<strong>Optional</strong>
</Typography>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>text</CardTitle>
<CardDescription>
Accessible label text for the trigger button. Defaults to "Toggle Sidebar".
</CardDescription>
</CardHeader>
<CardContent>
<Typography variant="body2" className="block mb-(--lsd-spacing-smaller)">
<strong>Type:</strong> <code>string</code>
</Typography>
<Typography variant="label1" className="block mt-(--lsd-spacing-smaller)">
<strong>Optional</strong>
</Typography>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>iconClassName</CardTitle>
<CardDescription>
Class name applied to the icon element. Defaults to "lsd:size-4/5".
</CardDescription>
</CardHeader>
<CardContent>
<Typography variant="body2" className="block mb-(--lsd-spacing-smaller)">
<strong>Type:</strong> <code>string</code>
</Typography>
<Typography variant="label1" className="block mt-(--lsd-spacing-smaller)">
<strong>Optional</strong>
</Typography>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>textClassName</CardTitle>
<CardDescription>
Class name applied to the text span element. Defaults to "lsd:sr-only".
</CardDescription>
</CardHeader>
<CardContent>
<Typography variant="body2" className="block mb-(--lsd-spacing-smaller)">
<strong>Type:</strong> <code>string</code>
</Typography>
<Typography variant="label1" className="block mt-(--lsd-spacing-smaller)">
<strong>Optional</strong>
</Typography>
</CardContent>
</Card>
</div>
</div>
</PageSection>

<PageSection title="Accessibility">
Expand Down
2 changes: 1 addition & 1 deletion packages/lsd-docs/e2e/sidebar-behavior.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ test.describe('Sidebar Behavior', () => {
await page.goto('/examples/sidebar/basic');

const _sidebar = page.locator('[data-slot="sidebar"]');
const trigger = page.locator('[data-sidebar="trigger"]');
const trigger = page.locator('[data-sidebar="trigger"]').first();

const rail = page.locator('[data-sidebar="rail"]');
await expect(rail).toBeVisible();
Expand Down
29 changes: 26 additions & 3 deletions packages/lsd/src/components/ui/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CaretLeftIcon, CaretRightIcon } from '@phosphor-icons/react';
import type * as React from 'react';
import {
Sheet,
Expand All @@ -7,6 +8,7 @@ import {
SheetTitle,
} from '@/components/ui/sheet';
import { cn } from '@/lib/utils';
import { SidebarTrigger } from './SidebarContent';
import { useSidebar } from './SidebarContext';
import { SIDEBAR_WIDTH_MOBILE, type SidebarProps } from './types';

Expand Down Expand Up @@ -67,12 +69,24 @@ export function Sidebar({
side = 'left',
variant = 'sidebar',
collapsible = 'offcanvas',
showTrigger = true,
triggerIconExpanded,
triggerIconCollapsed,
triggerClassName,
className,
children,
...props
}: SidebarProps) {
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();

const defaultTriggerIconExpanded = <CaretLeftIcon className="lsd:size-4/5" weight="duotone" />;
const defaultTriggerIconCollapsed = <CaretRightIcon className="lsd:size-4/5" weight="duotone" />;

const triggerIcon =
state === 'expanded'
? (triggerIconExpanded ?? defaultTriggerIconExpanded)
: (triggerIconCollapsed ?? defaultTriggerIconCollapsed);

if (collapsible === 'none') {
return (
<div
Expand Down Expand Up @@ -115,7 +129,7 @@ export function Sidebar({

return (
<div
className="lsd:group lsd:peer lsd:text-sidebar-foreground lsd:hidden lsd:md:block"
className="lsd:group lsd:peer lsd:text-sidebar-foreground lsd:hidden lsd:md:block lsd:relative"
data-state={state}
data-collapsible={state === 'collapsed' ? collapsible : ''}
data-variant={variant}
Expand All @@ -134,13 +148,22 @@ export function Sidebar({
: 'lsd:group-data-[collapsible=icon]:w-(--sidebar-width-icon)'
)}
/>
{showTrigger && (
<SidebarTrigger
className={
triggerClassName ??
'lsd:absolute lsd:top-2 lsd:-right-8 lsd:bg-lsd-surface lsd:z-1 lsd:border lsd:border-l-0 lsd:border-lsd-border'
}
icon={triggerIcon}
/>
)}
<div
data-slot="sidebar-container"
className={cn(
'lsd:fixed lsd:inset-y-0 lsd:z-10 lsd:hidden lsd:h-svh lsd:w-(--sidebar-width) lsd:transition-[left,right,width] lsd:duration-200 lsd:ease-linear lsd:md:flex',
side === 'left'
? 'lsd:left-0 lsd:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
: 'lsd:right-0 lsd:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
? 'lsd:left-0 lsd:group-data-[collapsible=offcanvas]:-left-(--sidebar-width)'
: 'lsd:right-0 lsd:group-data-[collapsible=offcanvas]:-right-(--sidebar-width)',
// Adjust the padding for floating and inset variants.
variant === 'floating' || variant === 'inset'
? 'lsd:p-(--lsd-spacing-smaller) lsd:group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'
Expand Down
16 changes: 13 additions & 3 deletions packages/lsd/src/components/ui/sidebar/SidebarContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,19 @@ export function SidebarInset({ className, ...props }: SidebarInsetProps) {
*
* @exportAs sub
*/
export function SidebarTrigger({ className, onClick, ...props }: SidebarTriggerProps) {
export function SidebarTrigger({
className,
onClick,
icon,
text = 'Toggle Sidebar',
iconClassName = 'lsd:size-4/5',
textClassName = 'lsd:sr-only',
...props
}: SidebarTriggerProps) {
const { toggleSidebar } = useSidebar();

const defaultIcon = <SidebarSimpleIcon className={iconClassName} weight="duotone" />;

return (
<Button
data-sidebar="trigger"
Expand All @@ -158,8 +168,8 @@ export function SidebarTrigger({ className, onClick, ...props }: SidebarTriggerP
}}
{...props}
>
<SidebarSimpleIcon className="lsd:size-4/5" weight="duotone" />
<span className="lsd:sr-only">Toggle Sidebar</span>
{icon ?? defaultIcon}
<span className={textClassName}>{text}</span>
</Button>
);
}
Expand Down
51 changes: 50 additions & 1 deletion packages/lsd/src/components/ui/sidebar/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,30 @@ export interface SidebarProps extends React.ComponentProps<'div'> {
* Controls how sidebar collapses: offcanvas (slides out), icon (shows only icons), or none (no collapse).
*/
collapsible?: 'offcanvas' | 'icon' | 'none';
/**
* Whether to show the sidebar trigger button.
*
* When true, displays the trigger button to toggle sidebar visibility.
*/
showTrigger?: boolean;
/**
* Icon element for the sidebar trigger when expanded.
*
* Passed to SidebarTrigger's icon prop when sidebar is expanded.
*/
triggerIconExpanded?: React.ReactNode;
/**
* Icon element for the sidebar trigger when collapsed.
*
* Passed to SidebarTrigger's icon prop when sidebar is collapsed.
*/
triggerIconCollapsed?: React.ReactNode;
/**
* Class name for the sidebar trigger button.
*
* Applied to the SidebarTrigger component.
*/
triggerClassName?: string;
}

export interface SidebarProviderProps extends React.ComponentProps<'div'> {
Expand Down Expand Up @@ -148,7 +172,32 @@ export interface SidebarMenuSkeletonProps extends React.ComponentProps<'div'> {
showIcon?: boolean;
}

export interface SidebarTriggerProps extends React.ComponentProps<typeof Button> {}
export interface SidebarTriggerProps extends React.ComponentProps<typeof Button> {
/**
* Icon element to display in the trigger button.
*
* Defaults to SidebarSimpleIcon with duotone weight.
*/
icon?: React.ReactNode;
/**
* Accessible label text for the trigger button.
*
* Defaults to "Toggle Sidebar".
*/
text?: string;
/**
* Class name applied to the icon element.
*
* Defaults to "lsd:size-4/5".
*/
iconClassName?: string;
/**
* Class name applied to the text span element.
*
* Defaults to "lsd:sr-only".
*/
textClassName?: string;
}

export interface SidebarRailProps extends React.ComponentProps<'button'> {}

Expand Down
Loading