插槽配置

通过插槽在预设位置注入自定义内容

类型定义

interface SlotsConfig {
  /**
   * 标题栏插槽
   */
  titleBar?: {
    left?: ReactNode;
    center?: ReactNode;
    right?: ReactNode;
  };

  /**
   * 侧边栏插槽
   */
  sidebar?: {
    header?: ReactNode;
    footer?: ReactNode;
    beforeNav?: ReactNode;
    afterNav?: ReactNode;
  };

  /**
   * Shell 插槽
   */
  shell?: {
    beforeContent?: ReactNode;
    afterContent?: ReactNode;
  };
}

标题栏插槽

在标题栏的不同位置注入内容:

┌─────────────────────────────────────────────────┐
│  [left]          [center]          [right] [×]  │
└─────────────────────────────────────────────────┘
slots: {
  titleBar: {
    left: <SearchInput />,
    center: <BreadcrumbNav />,
    right: <UserMenu />,
  },
},

示例:添加搜索框

function SearchInput() {
  return (
    <input
      type="search"
      placeholder="搜索..."
      className="h-7 w-48 rounded border px-2 text-sm"
    />
  );
}

slots: {
  titleBar: {
    left: <SearchInput />,
  },
},

侧边栏插槽

在侧边栏的不同位置注入内容:

┌────────────┐
│  [header]  │
├────────────┤
│[beforeNav] │
│            │
│  Nav Items │
│            │
│ [afterNav] │
├────────────┤
│  [footer]  │
└────────────┘
slots: {
  sidebar: {
    header: <WorkspaceSwitcher />,
    beforeNav: <QuickActions />,
    afterNav: <StorageIndicator />,
    footer: <UserProfile />,
  },
},

示例:添加用户信息

function UserProfile() {
  return (
    <div className="flex items-center gap-2 p-3 border-t">
      <img src="/avatar.png" className="w-8 h-8 rounded-full" />
      <div>
        <div className="text-sm font-medium">John Doe</div>
        <div className="text-xs text-muted-foreground">john@example.com</div>
      </div>
    </div>
  );
}

slots: {
  sidebar: {
    footer: <UserProfile />,
  },
},

示例:添加工作区切换器

function WorkspaceSwitcher() {
  return (
    <select className="w-full p-2 border-b text-sm">
      <option>个人工作区</option>
      <option>团队工作区</option>
    </select>
  );
}

slots: {
  sidebar: {
    header: <WorkspaceSwitcher />,
  },
},

Shell 插槽

在主内容区域前后注入内容:

┌────────────────────────────────────────┐
│            TitleBar                     │
├────────────┬───────────────────────────┤
│            │  [beforeContent]          │
│  Sidebar   ├───────────────────────────┤
│            │     Main Content          │
│            ├───────────────────────────┤
│            │  [afterContent]           │
└────────────┴───────────────────────────┘
slots: {
  shell: {
    beforeContent: <AlertBanner />,
    afterContent: <StatusBar />,
  },
},

示例:添加告警横幅

function AlertBanner() {
  return (
    <div className="bg-yellow-100 border-b border-yellow-200 px-4 py-2 text-sm text-yellow-800">
      系统将于今晚 22:00 进行维护
    </div>
  );
}

slots: {
  shell: {
    beforeContent: <AlertBanner />,
  },
},

示例:添加状态栏

function StatusBar() {
  return (
    <div className="border-t px-4 py-1 text-xs text-muted-foreground flex justify-between">
      <span>已连接</span>
      <span>最后同步: 5 分钟前</span>
    </div>
  );
}

slots: {
  shell: {
    afterContent: <StatusBar />,
  },
},

完整示例

import { WorkspaceSwitcher } from './components/WorkspaceSwitcher';
import { UserProfile } from './components/UserProfile';
import { SearchInput } from './components/SearchInput';
import { StatusBar } from './components/StatusBar';

export const config = {
  slots: {
    titleBar: {
      left: <SearchInput />,
    },
    sidebar: {
      header: <WorkspaceSwitcher />,
      footer: <UserProfile />,
    },
    shell: {
      afterContent: <StatusBar />,
    },
  },
};

注意事项

插槽内容是静态的,在配置时定义。如果需要动态内容,请在插槽组件内部处理状态。

// 正确:在组件内部管理状态
function DynamicCounter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

slots: {
  sidebar: {
    footer: <DynamicCounter />,
  },
},