@darksnow-ui/fractal-layout
v0.0.3
Published
Advanced panel management system for web applications with dynamic splitting, resizing, and recursive layouts
Downloads
11
Maintainers
Readme
@darksnow-ui/fractal-layout
✨ Features
- 🎯 Divisão Dinâmica - Divida qualquer painel horizontal ou verticalmente
- 📐 Redimensionamento Fluído - Arraste para redimensionar com constraints min/max
- 🔄 Layouts Recursivos - Crie layouts dentro de layouts infinitamente
- 🧩 Sistema de Widgets - Plugável e extensível
- 🎨 Totalmente Customizável - Estilos, comportamentos e configurações
- ⚡ Alta Performance - Otimizado para layouts complexos
- 📱 Responsivo - Funciona em desktop e mobile (em desenvolvimento)
📦 Instalação
npm install @darksnow-ui/fractal-layout
# ou
pnpm add @darksnow-ui/fractal-layout
# ou
yarn add @darksnow-ui/fractal-layout🚀 Quick Start
ResizablePanels Básico
import { ResizablePanels } from '@darksnow-ui/fractal-layout'
function App() {
const panels = [
{
id: "sidebar",
size: 30,
minSize: 20,
maxSize: 40,
children: <Sidebar />
},
{
id: "content",
size: 70,
children: <Content />
}
]
return (
<ResizablePanels
panels={panels}
direction="horizontal"
onSizeChange={(sizes) => console.log(sizes)}
/>
)
}FractalLayout Completo
import { FractalLayout } from '@darksnow-ui/fractal-layout'
// Defina seus widgets
const widgets = {
editor: {
id: "editor",
component: EditorWidget,
icon: FileTextIcon
},
terminal: {
id: "terminal",
component: TerminalWidget
},
preview: {
id: "preview",
component: PreviewWidget
}
}
// Layout inicial com props
const initialLayout = {
id: "root",
type: "layout",
direction: "horizontal",
children: [
{
id: "left",
type: "widget",
widget: "editor",
props: {
filename: "App.tsx",
language: "typescript",
theme: "dark"
},
size: 60
},
{
id: "right",
type: "widget",
widget: "preview",
props: {
url: "http://localhost:3000",
zoom: 0.8
},
size: 40
}
]
}
function App() {
const [layout, setLayout] = useState(initialLayout)
return (
<FractalLayout
layout={layout}
widgets={widgets}
onLayoutChange={setLayout}
config={{
enableContextMenu: true,
minPanelSize: 200
}}
/>
)
}📖 Documentação
ResizablePanels
Componente base para painéis redimensionáveis.
Props
| Prop | Tipo | Padrão | Descrição |
|------|------|--------|-----------|
| panels | Panel[] | - | Array de painéis a renderizar |
| direction | "horizontal" \| "vertical" | "horizontal" | Direção do layout |
| handleSize | number | 6 | Tamanho do handle em pixels |
| onSizeChange | (sizes: Record<string, number>) => void | - | Callback quando tamanhos mudam |
| className | string | - | Classes CSS adicionais |
Tipo Panel
interface Panel {
id: string
size?: number // Porcentagem (0-100)
minSize?: number // Tamanho mínimo
maxSize?: number // Tamanho máximo
children: ReactNode
}FractalLayout
Sistema completo de gerenciamento de layouts com recursos avançados.
Props
| Prop | Tipo | Descrição |
|------|------|-----------|
| layout | FractalPanel | Estrutura do layout |
| widgets | WidgetRegistry | Registro de widgets disponíveis |
| onLayoutChange | (layout: FractalPanel) => void | Callback para mudanças |
| config | FractalConfig | Configurações opcionais |
Tipos Principais
interface FractalPanel {
id: string
type: "widget" | "layout"
widget?: string // ID do widget
props?: Record<string, any> // Props específicas para a instância do widget
children?: FractalPanel[] // Sub-painéis
direction?: "horizontal" | "vertical"
size?: number
minSize?: number
maxSize?: number
}
interface Widget {
id: string
component: React.ComponentType
icon?: React.ComponentType
config?: Record<string, any>
}
interface FractalConfig {
minPanelSize?: number // Default: 300
scrollThreshold?: number // Default: 3
enableContextMenu?: boolean // Default: true
handleSize?: number // Default: 6
}🎮 Exemplos Avançados
Persistência de Layout
function PersistentLayout() {
const [layout, setLayout] = useState(() => {
const saved = localStorage.getItem('app-layout')
return saved ? JSON.parse(saved) : defaultLayout
})
const handleLayoutChange = (newLayout) => {
setLayout(newLayout)
localStorage.setItem('app-layout', JSON.stringify(newLayout))
}
return <FractalLayout layout={layout} onLayoutChange={handleLayoutChange} />
}Undo/Redo
function LayoutWithHistory() {
const [history, setHistory] = useState([initialLayout])
const [index, setIndex] = useState(0)
const layout = history[index]
const handleChange = (newLayout) => {
const newHistory = history.slice(0, index + 1)
newHistory.push(newLayout)
setHistory(newHistory)
setIndex(newHistory.length - 1)
}
const undo = () => index > 0 && setIndex(index - 1)
const redo = () => index < history.length - 1 && setIndex(index + 1)
return (
<>
<button onClick={undo} disabled={index === 0}>Undo</button>
<button onClick={redo} disabled={index === history.length - 1}>Redo</button>
<FractalLayout layout={layout} onLayoutChange={handleChange} />
</>
)
}Widgets com Props Customizadas
Uma das features mais poderosas é a capacidade de passar props específicas para cada instância de widget através do campo props:
// Definir um widget que aceita props
const ChartWidget = ({ title, data, type = 'bar', color }) => {
return (
<div>
<h3>{title}</h3>
<Chart type={type} data={data} color={color} />
</div>
)
}
// Registrar o widget uma única vez
const widgets = {
chart: {
id: 'chart',
component: ChartWidget
}
}
// Criar múltiplas instâncias com configurações diferentes
const layout = {
id: "root",
type: "layout",
direction: "horizontal",
children: [
{
id: "sales-chart",
type: "widget",
widget: "chart",
props: {
title: "Vendas 2024",
type: "bar",
data: salesData,
color: "#3b82f6"
},
size: 50
},
{
id: "growth-chart",
type: "widget",
widget: "chart",
props: {
title: "Crescimento Anual",
type: "line",
data: growthData,
color: "#10b981"
},
size: 50
}
]
}Persistência e Identificação
Cada widget pode receber um ID único através das props, permitindo:
const StatefulWidget = ({ id, initialValue }) => {
// Usar o ID para persistir estado
const [value, setValue] = usePersistedState(`widget-${id}`, initialValue)
// Buscar dados específicos
const { data } = useWidgetData(id)
return <div>...</div>
}
// No layout
{
widget: "stateful",
props: {
id: "metrics-dashboard-main",
initialValue: { view: 'monthly' }
}
}Widget Recursivo
const LayoutWidget = () => {
const nestedLayout = {
id: "nested",
type: "layout",
direction: "vertical",
children: [/* ... */]
}
return (
<div className="nested-layout-widget">
<h4>Sub-layout</h4>
<FractalLayout
layout={nestedLayout}
widgets={widgets}
config={{ enableContextMenu: false }}
/>
</div>
)
}
// Registrar no widgets
const widgets = {
// ... outros widgets
layout: {
id: "layout",
component: LayoutWidget,
acceptsChildren: true
}
}🎨 Customização
CSS Variables
:root {
--fractal-border: #e2e8f0;
--fractal-handle-color: #cbd5e1;
--fractal-handle-hover: #94a3b8;
--fractal-radius: 0.5rem;
}Estilos Customizados
<FractalLayout
className="my-custom-layout"
layout={layout}
widgets={widgets}
/>.my-custom-layout .resize-handle {
background: linear-gradient(90deg, #3b82f6, #8b5cf6);
}
.my-custom-layout .widget-panel {
border-radius: 1rem;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
}🛠️ Desenvolvimento
Setup
# Clone o repositório
git clone https://github.com/darksnow-ui/fractal-layout
# Instale dependências
pnpm install
# Rode a demo
pnpm demoScripts
pnpm build # Build da biblioteca
pnpm test # Rodar testes
pnpm demo # Demo interativa (porta 3000)
pnpm typecheck # Verificar tiposEstrutura
fractal-layout/
├── src/
│ ├── components/
│ │ ├── ResizablePanels/ # Componente base
│ │ └── FractalLayout/ # Sistema completo
│ ├── demo/ # Aplicação de demonstração
│ └── index.ts # Exports principais
├── docs/ # Documentação detalhada
└── package.json🤝 Contribuindo
Contribuições são bem-vindas! Por favor:
- Fork o projeto
- Crie uma branch para sua feature (
git checkout -b feature/AmazingFeature) - Commit suas mudanças (
git commit -m 'Add some AmazingFeature') - Push para a branch (
git push origin feature/AmazingFeature) - Abra um Pull Request
📄 Licença
MIT © DarkSnow UI
🙏 Agradecimentos
Inspirado por:
- React Resizable Panels
- VSCode Split View
- tmux
Feito com ❤️ pela equipe DarkSnow UI
