Skip to content
This repository was archived by the owner on Jul 31, 2026. It is now read-only.
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
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/components/Icon/names.ts
31 changes: 31 additions & 0 deletions src/components/AppLayout/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { createContext } from 'react'

export interface KeybindConfig {
/**
* The keybind to trigger the action
* Will be CMDOrCTRL + key
*/
key: string
description: string
}

export interface AppLayoutContextType {
collapsed: boolean
setCollapsed: (collapsed: boolean) => void

// keybind config
keybinds: {
toggle: KeybindConfig
}
}

export const AppLayoutContext = createContext<AppLayoutContextType>({
collapsed: false,
setCollapsed: () => {},
keybinds: {
toggle: {
key: 'L',
description: 'Toggle',
},
},
})
198 changes: 198 additions & 0 deletions src/components/AppLayout/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import { Meta, StoryObj } from '@storybook/react'
import { AppLayout } from '.'
import { AppLayoutProvider } from './provider'
import { Heading } from '../Heading'
import { Text } from '../Text'
import { Icon } from '../Icon'
import { Button } from '../Button'
import { motion } from 'framer-motion'
import React from 'react'

type Story = StoryObj<typeof AppLayout>

const meta: Meta<typeof AppLayout> = {
title: 'Components/AppLayout',
component: AppLayout,
tags: ['autodocs'],
parameters: {
layout: 'fullscreen',
},
decorators: [
(Story) => (
<div className="h-svh w-full">
<Story />
</div>
),
],
}

export default meta

const SurfaceContent = () => {
return (
<React.Fragment>
<Heading>Hello this is a surface</Heading>
<div className="flex flex-col gap-1">
<Text variant="md">A surface is like a piece of paper.</Text>
<Text variant="md">
A surface should have a tactile feel to it, like your first school
diary. It should evoke a sense of nostalgia and warmth.
</Text>
</div>
</React.Fragment>
)
}

export const Default: Story = {
args: {
children: [
<AppLayout.Sidebar key="sidebar">
{(collapsed) => (
<div className="flex flex-col">
<div className="text-muted-foreground hover:text-foreground flex h-9 cursor-pointer items-center gap-3">
<Icon name="house" className="size-6" strokeWidth={1.25} />
{collapsed ? null : (
<motion.span
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}
>
Home
</motion.span>
)}
</div>
<div className="text-muted-foreground hover:text-foreground flex h-9 cursor-pointer items-center gap-3">
<Icon name="settings" className="size-6" strokeWidth={1.25} />
{collapsed ? null : (
<motion.span
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}
>
Settings
</motion.span>
)}
</div>
<div className="text-muted-foreground hover:text-foreground flex h-9 cursor-pointer items-center gap-3">
<Icon name="users" className="size-6" strokeWidth={1.25} />
{collapsed ? null : (
<motion.span
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}
>
Users
</motion.span>
)}
</div>
</div>
)}
</AppLayout.Sidebar>,
<AppLayout.SurfaceHeader key="surface-header">
<AppLayout.CollapseButton />
<AppLayout.HeaderDivider />
<AppLayout.Breadcrumb>
<AppLayout.BreadcrumbItem>Home</AppLayout.BreadcrumbItem>
<AppLayout.BreadcrumbItem active>Settings</AppLayout.BreadcrumbItem>
</AppLayout.Breadcrumb>
</AppLayout.SurfaceHeader>,
<AppLayout.Surface className="p-4" key="surface">
<SurfaceContent />
</AppLayout.Surface>,
],
},
render: (args) => (
<AppLayoutProvider defaultCollapsed>
<AppLayout {...args} />
</AppLayoutProvider>
),
}

export const WithHeader: Story = {
name: 'With Header',
args: {
children: [
<AppLayout.Header key="header" className="p-3">
<Text variant="sm">This layout is in beta.</Text>
</AppLayout.Header>,
<AppLayout.Sidebar key="sidebar">
{(collapsed) => (
<div className="flex flex-col">
<div className="text-muted-foreground hover:text-foreground flex h-9 cursor-pointer items-center gap-3">
<Icon name="house" className="size-6" strokeWidth={1.25} />
{collapsed ? null : <span>Home</span>}
</div>
<div className="text-muted-foreground hover:text-foreground flex h-9 cursor-pointer items-center gap-3">
<Icon name="settings" className="size-6" strokeWidth={1.25} />
{collapsed ? null : <span>Settings</span>}
</div>
<div className="text-muted-foreground hover:text-foreground flex h-9 cursor-pointer items-center gap-3">
<Icon name="users" className="size-6" strokeWidth={1.25} />
{collapsed ? null : <span>Users</span>}
</div>
</div>
)}
</AppLayout.Sidebar>,
<AppLayout.SurfaceHeader key="surface-header">
<AppLayout.CollapseButton />
<div className="mr-1 ml-auto flex items-center gap-3">
<Button variant="outline">
<Icon name="circle-plus" className="size-4" strokeWidth={1.25} />
<span>Add new</span>
</Button>
</div>
</AppLayout.SurfaceHeader>,
<AppLayout.Surface className="p-4" key="surface">
<SurfaceContent />
</AppLayout.Surface>,
],
},
render: (args) => (
<AppLayoutProvider>
<AppLayout {...args} />
</AppLayoutProvider>
),
}

export const CustomSurfaceHeader: Story = {
name: 'Custom Surface Header',
args: {
children: [
<AppLayout.Sidebar key="sidebar">
{(collapsed) => (
<div className="flex flex-col">
<div className="text-muted-foreground hover:text-foreground flex h-9 cursor-pointer items-center gap-3">
<Icon name="house" className="size-6" strokeWidth={1.25} />
{collapsed ? null : <span>Home</span>}
</div>
<div className="text-muted-foreground hover:text-foreground flex h-9 cursor-pointer items-center gap-3">
<Icon name="settings" className="size-6" strokeWidth={1.25} />
{collapsed ? null : <span>Settings</span>}
</div>
<div className="text-muted-foreground hover:text-foreground flex h-9 cursor-pointer items-center gap-3">
<Icon name="users" className="size-6" strokeWidth={1.25} />
{collapsed ? null : <span>Users</span>}
</div>
</div>
)}
</AppLayout.Sidebar>,
<AppLayout.SurfaceHeader key="surface-header">
<AppLayout.CollapseButton />
<div className="mr-1 ml-auto flex items-center gap-3">
<Button variant="outline">
<Icon name="circle-plus" className="size-4" strokeWidth={1.25} />
<span>Add new</span>
</Button>
</div>
</AppLayout.SurfaceHeader>,
<AppLayout.Surface className="p-4" key="surface">
<SurfaceContent />
</AppLayout.Surface>,
],
},
render: (args) => (
<AppLayoutProvider defaultCollapsed>
<AppLayout {...args} />
</AppLayoutProvider>
),
}
Loading