AppLayout
AppLayout は管理画面のシェル全体です: グラデーションのトップバー(検索、メッセージ、通知、言語、カラーモード、ユーザーメニュー)、アクティブな階層を示す多階層のサイドメニュー、ユーザープロフィール、設定パネル、ページヘッダー、フッター。config.layout で vertical、mini、horizontal を切り替えます。
プレビュー
<div class="layout-frame"><iframe src="http://localhost:5182/" title="Vue playground" loading="lazy"></iframe><p class="layout-frame__hint">Live Vue playground (run <code>npm run dev:vue</code>). Open the ⚙ settings panel to switch skin, layout, dark mode and language.</p></div>import { AppLayout, Page } from '@thl-ui/react';
import { Link, useLocation } from 'react-router';
<AppLayout menu={menu} currentPath={loc.pathname} user={{ ...user, onSelect: (k) => k === 'logout' && logout() }}
onSearch={(q) => nav(`/search?q=${q}`)}
renderLink={(item, children, className) => <Link to={item.href!} className={className}>{children}</Link>}
topbarRight={<TopbarMessages items={messages} icon="mail" />}
sidebarFooter={<Button variant="success" block>Upgrade</Button>}>
<Page title="Dashboard" breadcrumb={[{ label: 'Home', href: '/' }, { label: 'Dashboard' }]}>…</Page>
</AppLayout><thl-app-layout [menu]="menu" [currentPath]="path()" [user]="user" searchable (search)="search($event)" (userSelect)="onUser($event)">
<ng-template thlMenuLink let-item let-cls="cls" let-content="content">
<a [routerLink]="item.href" [class]="cls"><ng-container [ngTemplateOutlet]="content" /></a>
</ng-template>
<ng-container thlTopbarRight><thl-topbar-messages [items]="messages" icon="mail" /></ng-container>
<div thlSidebarFooter><button thl-button variant="success" block>Upgrade</button></div>
<router-outlet />
</thl-app-layout>Vue
<ThlAppLayout :menu="menu" :current-path="route.path" :user="user" searchable @search="onSearch" @user-select="onUser">
<template #topbar-right><ThlTopbarMessages :items="messages" icon="mail" /></template>
<template #link="{ item, content, class: cls }"><RouterLink :to="item.href" :class="cls"><component :is="() => content" /></RouterLink></template>
<template #sidebar-footer><ThlButton variant="success" block>アップグレード</ThlButton></template>
<ThlPage title="Dashboard" :breadcrumb="[{ label: 'ホーム', href: '/' }, { label: 'Dashboard' }]">…</ThlPage>
</ThlAppLayout>メニュー
import type { MenuItem } from '@thl-ui/core';
export const menu: MenuItem[] = [
{ key: 'dash', label: 'menu.dashboard', icon: 'layout-dashboard', badge: { text: 4, color: 'danger' }, children: [
{ key: 'd1', label: 'menu.dashboard1', href: '/' },
{ key: 'd2', label: 'menu.dashboard2', href: '/dashboard-2' },
] },
{ key: 'h', label: 'menu.apps', heading: true },
{ key: 'inbox', label: 'menu.inbox', icon: 'mail', href: '/inbox' },
];label には i18n キー(自動的に翻訳されます)または文字列リテラルを指定できます。アクティブな階層は currentPath から計算されます。
水平レイアウト
layout: 'horizontal' にすると、メニューはサイドからトップバー下のバーに移動します(Cubic-horizontal と同じ)。サブメニューはホバーまたはキーボードフォーカスでドロップダウンとして開きます。メニューは常に 1 行に収まり、入りきらない項目は もっと見る ドロップダウンにまとめられ、ウィンドウを広げると元に戻ります。768px 未満では通常のオフキャンバスサイドバーになります。同じ menu 配列をすべてのレイアウトで使えます。
// main.tsx: 水平モードで起動
<ThlProvider config={{ layout: 'horizontal' }}>
<AppLayout menu={menu} currentPath={loc.pathname} renderLink={renderLink}>…</AppLayout>
</ThlProvider>
// 実行時に切り替え(⚙ 設定パネルも同じ処理)
const [, setConfig] = useThlConfig();
setConfig({ layout: 'horizontal' });// main.ts: 水平モードで起動
app.use(ThlUI, { layout: 'horizontal' });
// 実行時に切り替え(⚙ 設定パネルも同じ処理)
const { set } = useThlConfig();
set({ layout: 'horizontal' });// main.ts: 水平モードで起動
bootstrapApplication(AppComponent, { providers: [provideThlUI({ layout: 'horizontal' })] });
// 実行時に切り替え(⚙ 設定パネルも同じ処理)
inject(ThlConfigService).set({ layout: 'horizontal' });プレイグラウンドで試す: localhost:5182/?layout=horizontal。?layout=mini、?theme=red-dark、?lang=ja、?mode=dark も指定できます。
レスポンシブな動作
- ≥ 1170px: フルサイドバー(または
layoutに応じて mini / horizontal) - 768 – 1169px: 自動的にミニサイドバー
- < 768px: 背景付きのオフキャンバスサイドバー。トップバーから開閉
ブレークポイントは components.Sidebar.breakpoint で設定できます。
API
AppLayout
<ThlAppLayout> · import { ThlAppLayout } from '@thl-ui/vue'
| Prop | 型 | デフォルト |
|---|---|---|
menu* | array | — |
currentPath | string | — |
brand | string | "thl-ui" |
brandHref | string | "/" |
user | object | — |
searchable | boolean | — |
settings | boolean | true |
footer | string | boolean | — |
イベント: searchuser-select
スロット: brandtopbar-lefttopbar-rightlinksidebar-footerdefaultfooter
SideMenu
<ThlSideMenu> · import { ThlSideMenu } from '@thl-ui/vue'
| Prop | 型 | デフォルト |
|---|---|---|
items* | array | — |
currentPath | string | — |
accordion | boolean | true |
horizontal | boolean | — |
スロット: link
TopbarMessages
<ThlTopbarMessages> · import { ThlTopbarMessages } from '@thl-ui/vue'
| Prop | 型 | デフォルト |
|---|---|---|
items* | array | — |
icon | string | "mail" |
title | string | — |
footer | string | — |
footerHref | string | — |
SettingsPanel
<ThlSettingsPanel> · import { ThlSettingsPanel } from '@thl-ui/vue'
| Prop | 型 | デフォルト |
|---|---|---|
open | boolean | — |
イベント: update:open
Page
<ThlPage> · import { ThlPage } from '@thl-ui/vue'
| Prop | 型 | デフォルト |
|---|---|---|
title | string | — |
breadcrumb | array | — |
スロット: actionslinkdefault