-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #77 from brainstormforce/SUR-279-sidebars
SUR-279 Sidebars
- Loading branch information
Showing
11 changed files
with
342 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
<?php return array('dependencies' => array('react', 'react-dom'), 'version' => '7dc7b1b28db824d70a01'); | ||
<?php return array('dependencies' => array('react', 'react-dom'), 'version' => '7a061455c0fab83c29c7'); |
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { Sidebar, SidebarHeader, SidebarBody, SidebarFooter, SidebarItem } from './sidebar.jsx'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# Sidebar Component Documentation | ||
|
||
## Description | ||
|
||
The `Sidebar` component is a flexible left panel that enhances user navigation, featuring a `Header` for titles, a `Body` for interactive elements, and a `Footer` for additional actions. Customizable with props like `className` and `screenHeight`, it adapts to various screen sizes, ensuring optimal usability on both desktop and mobile devices. | ||
|
||
## `Sidebar` Props | ||
|
||
### `children` | ||
- **Type:** `ReactNode` | ||
- **Description:** Elements to render inside the Sidebar container, typically the `SidebarHeader`, `SidebarBody`, and `SidebarFooter` components. | ||
|
||
### `className` | ||
- **Type:** `string` | ||
- **Description:** Additional classes to customize the Sidebar container's styles. | ||
|
||
### `onCollapseChange` | ||
- **Type:** `(collapsed: boolean) => void` | ||
- **Description:** Callback function triggered when the Sidebar's collapse state changes. Use this to handle custom logic based on whether the Sidebar is collapsed or expanded. | ||
|
||
### `screenHeight` | ||
- **Type:** `boolean` | ||
- **default value:** `true` | ||
- **Description:** Controls whether the Sidebar should take up the full screen height. If `true`, the Sidebar will have a height equal to the viewport. | ||
|
||
### `borderOn` | ||
- **Type:** `boolean` | ||
- **default value:** `true` | ||
- **Description:** Controls whether the Sidebar should have border. If `true`, the Sidebar will have a border on the right. | ||
|
||
### `collapsible` | ||
- **Type:** `boolean` | ||
- **default value:** `true` | ||
- **Description:** Controls whether the Sidebar should collapsible. If `true`, the Sidebar will have a collapse button. | ||
|
||
## `SidebarHeader` Props | ||
|
||
### `children` | ||
- **Type:** `ReactNode` | ||
- **Description:** Elements to render inside the `SidebarHeader` container, usually icons, logos, or navigation items. | ||
|
||
## `SidebarBody` Props | ||
|
||
### `children` | ||
- **Type:** `ReactNode` | ||
- **Description:** Elements to render inside the `SidebarBody` container, usually navigation links, buttons, or other interactive elements. | ||
|
||
## `SidebarFooter` Props | ||
|
||
### `children` | ||
- **Type:** `ReactNode` | ||
- **Description:** Elements to render inside the `SidebarFooter` container, typically icons, user profile, badges, or help buttons. | ||
|
||
## `SidebarItem` Props | ||
|
||
### `children` | ||
- **Type:** `ReactNode` | ||
- **Description:** Content or components to render inside the `SidebarItem`. | ||
|
||
### `className` | ||
- **Type:** `string` | ||
- **Description:** Additional classes to customize the Item styling. | ||
|
||
```jsx | ||
<Sidebar> | ||
<SidebarHeader> | ||
<SidebarItem> | ||
<Logo /> | ||
</SidebarItem> | ||
</SidebarHeader> | ||
|
||
<SidebarBody align="Header"> | ||
<SidebarItem> | ||
<div className='flex gap-2'> | ||
<div>Nav Item 1</div> | ||
<div>Nav item 2</div> | ||
<div>Nav Item 3</div> | ||
</div> | ||
</SidebarItem> | ||
<SidebarItem> | ||
<Button>Upgrade to Pro</Button> | ||
</SidebarItem> | ||
</SidebarBody> | ||
|
||
<SidebarFooter> | ||
<SidebarItem> | ||
<Badge /> | ||
</SidebarItem> | ||
<SidebarItem> | ||
<Avatar /> | ||
</SidebarItem> | ||
</SidebarFooter> | ||
</Sidebar> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import React, { | ||
createContext, | ||
useContext, | ||
useState, | ||
useRef, | ||
useEffect, | ||
} from 'react'; | ||
import { cn } from '@/utilities/functions'; | ||
import { PanelLeftClose, PanelLeftOpen } from 'lucide-react'; | ||
import Tooltip from '../tooltip'; | ||
const SidebarContext = createContext(); | ||
|
||
const Sidebar = ( { | ||
children, | ||
className, | ||
onCollapseChange, | ||
collapsible = true, | ||
screenHeight = true, | ||
borderOn = true, | ||
...props | ||
} ) => { | ||
const sideBarRef = useRef( null ); | ||
const [ isCollapsed, setIsCollapsed ] = useState( () => { | ||
const storedState = localStorage.getItem( 'sidebar-collapsed' ); | ||
const isSmallScreen = window.innerWidth < 1280; | ||
if ( storedState ) { | ||
return JSON.parse( storedState ); | ||
} | ||
return isSmallScreen; | ||
} ); | ||
|
||
useEffect( () => { | ||
if ( onCollapseChange ) { | ||
onCollapseChange( isCollapsed ); | ||
} | ||
}, [ isCollapsed, onCollapseChange ] ); | ||
|
||
useEffect( () => { | ||
const handleScreenResize = () => { | ||
const isSmallScreen = window.innerWidth < 1280; | ||
if ( isSmallScreen ) { | ||
setIsCollapsed( true ); | ||
|
||
localStorage.setItem( 'sidebar-collapsed', JSON.stringify( true ) ); | ||
} else { | ||
const storedState = localStorage.getItem( 'sidebar-collapsed' ); | ||
setIsCollapsed( storedState ? JSON.parse( storedState ) : false ); | ||
} | ||
|
||
if ( sideBarRef.current ) { | ||
if ( !! screenHeight ) { | ||
sideBarRef.current.style.height = `${ window.innerHeight }px`; | ||
} else { | ||
sideBarRef.current.style.height = 'auto'; | ||
} | ||
} | ||
}; | ||
|
||
window.addEventListener( 'resize', handleScreenResize ); | ||
handleScreenResize(); | ||
|
||
return () => { | ||
window.removeEventListener( 'resize', handleScreenResize ); | ||
}; | ||
}, [ screenHeight ] ); | ||
|
||
return ( | ||
<SidebarContext.Provider value={ { isCollapsed, setIsCollapsed, collapsible } }> | ||
<div | ||
ref={ sideBarRef } | ||
className={ cn( | ||
'overflow-auto w-72 px-4 py-4 gap-4 flex flex-col bg-background-primary', | ||
borderOn && | ||
'border-0 border-r border-solid border-border-subtle', | ||
!! screenHeight && 'h-screen', | ||
'transition-all duration-200', | ||
isCollapsed && 'w-16 px-2', | ||
className | ||
) } | ||
{ ...props } | ||
> | ||
{ children } | ||
</div> | ||
</SidebarContext.Provider> | ||
); | ||
}; | ||
|
||
const SidebarHeader = ( { children } ) => { | ||
return <div className="space-y-2">{ children }</div>; | ||
}; | ||
|
||
const SidebarBody = ( { children } ) => { | ||
return <div className={ cn( 'space-y-4 grow items-start' ) }>{ children }</div>; | ||
}; | ||
|
||
const SidebarFooter = ( { children } ) => { | ||
const { isCollapsed, setIsCollapsed, collapsible } = | ||
useContext( SidebarContext ); | ||
return ( | ||
<div className="space-y-4"> | ||
{ children } | ||
{ collapsible && ( | ||
<button | ||
className={ cn( | ||
'bg-transparent w-full border-0 p-0 m-0 flex items-center gap-2 text-base cursor-pointer', | ||
isCollapsed && 'justify-center' | ||
) } | ||
onClick={ () => { | ||
setIsCollapsed( ! isCollapsed ); | ||
|
||
localStorage.setItem( | ||
'sidebar-collapsed', | ||
JSON.stringify( ! isCollapsed ) | ||
); | ||
} } | ||
aria-label={ | ||
isCollapsed ? 'Expand sidebar' : 'Collapse sidebar' | ||
} | ||
> | ||
{ isCollapsed ? ( | ||
<> | ||
<Tooltip title="Expand" placement="right"> | ||
<PanelLeftOpen className="size-5" /> | ||
</Tooltip> | ||
</> | ||
) : ( | ||
<> | ||
<PanelLeftClose className="size-5" /> Collapse | ||
</> | ||
) } | ||
</button> | ||
) } | ||
</div> | ||
); | ||
}; | ||
|
||
const SidebarItem = ( { children, className } ) => { | ||
return <div className={ cn( 'w-full', className ) }>{ children }</div>; | ||
}; | ||
|
||
export { Sidebar, SidebarHeader, SidebarBody, SidebarFooter, SidebarItem }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { Sidebar, SidebarHeader, SidebarBody, SidebarFooter, SidebarItem } from './sidebar'; | ||
import Button from '../button'; | ||
|
||
export default { | ||
title: 'Organism/Sidebar', | ||
component: Sidebar, | ||
parameters: { | ||
layout: 'left', | ||
}, | ||
tags: [ 'autodocs' ], | ||
argTypes: { | ||
children: { | ||
description: 'Content to render inside the Sidebar. This typically includes `SidebarHeader`, `SidebarBody`, and `SidebarFooter` components.', | ||
control: { type: 'none' }, | ||
}, | ||
className: { | ||
description: 'Optional custom CSS classes to apply to the Sidebar container for styling.', | ||
control: { type: 'text' }, | ||
table: { | ||
type: { summary: 'string' }, | ||
}, | ||
}, | ||
borderOn: { | ||
description: 'Controls whether a border should appear on the right of the Sidebar.', | ||
control: { type: 'boolean' }, | ||
defaultValue: true, | ||
table: { | ||
type: { summary: 'boolean' }, | ||
defaultValue: { summary: true }, | ||
}, | ||
}, | ||
collapsible: { | ||
description: 'Determines if the Sidebar can be collapsed or not. If `true`, a collapse button is shown.', | ||
control: { type: 'boolean' }, | ||
defaultValue: true, | ||
table: { | ||
type: { summary: 'boolean' }, | ||
defaultValue: { summary: true }, | ||
}, | ||
}, | ||
screenHeight: { | ||
description: 'Determines whether the Sidebar should occupy the full screen height.', | ||
control: { type: 'boolean' }, | ||
defaultValue: true, | ||
table: { | ||
type: { summary: 'boolean' }, | ||
defaultValue: { summary: true }, | ||
}, | ||
}, | ||
onCollapseChange: { | ||
description: 'Callback function triggered when the Sidebar collapse state changes. Use this to handle logic based on collapse/expand states.', | ||
action: 'onCollapseChange', | ||
}, | ||
}, | ||
}; | ||
|
||
const Template = ( args ) => ( | ||
<Sidebar { ...args } | ||
> | ||
<SidebarHeader> | ||
<SidebarItem> | ||
<img | ||
width="240px" | ||
alt="Logo" | ||
src="https://upload.wikimedia.org/wikipedia/commons/4/44/Hyundai_Motor_Company_logo.svg" | ||
/> | ||
</SidebarItem> | ||
</SidebarHeader> | ||
<SidebarBody> | ||
<SidebarItem> | ||
<div className="flex flex-col gap-2"> | ||
{ [ 1, 2, 3, 4, 5, 6 ].map( ( num ) => ( | ||
<div key={ num }>Nav Item</div> | ||
) ) } | ||
</div> | ||
|
||
</SidebarItem> | ||
|
||
</SidebarBody> | ||
<SidebarFooter> | ||
<Button className="w-full"> | ||
Pro | ||
</Button> | ||
</SidebarFooter> | ||
</Sidebar> | ||
); | ||
|
||
export const DefaultSidebar = Template.bind( {} ); | ||
DefaultSidebar.args = { | ||
screenHeight: true, | ||
borderOn: true, | ||
collapsible: true, | ||
}; | ||
|
||
DefaultSidebar.storyName = 'Sidebar'; | ||
|