@morya-ui/mcp 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/catalog.json +79 -39
- package/data/example-coverage.json +4 -6
- package/dist/__tests__/tools.test.js +49 -3
- package/dist/decisions.js +70 -0
- package/dist/page-snippets.js +49 -0
- package/dist/patterns.d.ts +25 -3
- package/dist/patterns.js +128 -8
- package/dist/tools.js +94 -39
- package/package.json +1 -1
package/data/catalog.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-09-
|
|
2
|
+
"generatedAt": "2026-09-14T07:45:14.694Z",
|
|
3
3
|
"library": {
|
|
4
4
|
"name": "morya-ui",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.4"
|
|
6
6
|
},
|
|
7
7
|
"mcp": {
|
|
8
8
|
"name": "@morya-ui/mcp",
|
|
9
|
-
"version": "0.2.
|
|
9
|
+
"version": "0.2.4"
|
|
10
10
|
},
|
|
11
11
|
"components": [
|
|
12
12
|
{
|
|
@@ -4400,6 +4400,11 @@
|
|
|
4400
4400
|
"title": "Nested + useContextMenu",
|
|
4401
4401
|
"body": "```vue preview\n<script setup lang=\"ts\">\nimport { useContextMenu, MContextMenu } from 'morya-ui'\n\nconst menu = useContextMenu()\nconst model = [\n { label: '复制', command: () => undefined },\n { label: '更多', items: [{ label: '深层' }] },\n]\n</script>\n\n<template>\n <div\n style=\"border: 1px dashed var(--m-color-border); padding: 2rem; border-radius: var(--m-radius-md)\"\n @contextmenu=\"menu.show\"\n >\n 右键此处(composable)\n </div>\n <MContextMenu v-model=\"menu.visible\" v-model:position=\"menu.position\" :model=\"model\" />\n</template>\n```"
|
|
4402
4402
|
},
|
|
4403
|
+
{
|
|
4404
|
+
"id": "滚动与嵌套子菜单",
|
|
4405
|
+
"title": "滚动与嵌套子菜单",
|
|
4406
|
+
"body": "菜单列表与嵌套 `items` 飞出层使用内置 `MScrollbar`(`max-height: min(18rem, 45vh)`)。嵌套层 Teleport 到 `body` 相对父项定位,避免长列表滚动时侧向菜单被裁剪。"
|
|
4407
|
+
},
|
|
4403
4408
|
{
|
|
4404
4409
|
"id": "props",
|
|
4405
4410
|
"title": "Props",
|
|
@@ -4426,7 +4431,7 @@
|
|
|
4426
4431
|
"body": "<h4 id=\"ContextMenuItem\">ContextMenuItem</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ninterface ContextMenuItem extends MenuNodeBase {\n items?: ContextMenuItem[]\n}\n```"
|
|
4427
4432
|
}
|
|
4428
4433
|
],
|
|
4429
|
-
"markdown": "---\ntitle: ContextMenu\ncategory: 04 / NAVIGATION\ndescription: 右键上下文菜单,支持 show(event) / hide()。\n---\n\n# ContextMenu\n\n在指针位置弹出的上下文菜单。支持嵌套 `items`。也可用 `useContextMenu()` 绑定 `v-model` / `v-model:position`。\n\n## 引入\n\n```ts\nimport { useContextMenu, MContextMenu } from 'morya-ui'\n```\n\n## 基础用法\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MContextMenu } from 'morya-ui'\n\nconst model = [\n { label: '复制' },\n { label: '粘贴' },\n { separator: true },\n { label: '删除', disabled: true },\n]\n</script>\n\n<template>\n <MContextMenu :model=\"model\">\n <div\n style=\"border: 1px dashed var(--m-color-border); padding: 2rem; border-radius: var(--m-radius-md)\"\n >\n 右键此处打开菜单\n </div>\n </MContextMenu>\n</template>\n```\n\n## Nested + useContextMenu\n\n```vue preview\n<script setup lang=\"ts\">\nimport { useContextMenu, MContextMenu } from 'morya-ui'\n\nconst menu = useContextMenu()\nconst model = [\n { label: '复制', command: () => undefined },\n { label: '更多', items: [{ label: '深层' }] },\n]\n</script>\n\n<template>\n <div\n style=\"border: 1px dashed var(--m-color-border); padding: 2rem; border-radius: var(--m-radius-md)\"\n @contextmenu=\"menu.show\"\n >\n 右键此处(composable)\n </div>\n <MContextMenu v-model=\"menu.visible\" v-model:position=\"menu.position\" :model=\"model\" />\n</template>\n```\n\n## Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `model` | `ContextMenuItem[]` | — | 菜单项,可嵌套 `items`;项可含 `key` / `icon`。 |\n| `modelValue` | `boolean` | `false` | 是否可见。 |\n| `position` | `{ x: number; y: number }` | — | 菜单坐标。 |\n| `teleport` | `boolean` | `true` | 浮层 Teleport;默认挂到 `body`。 |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | 挂载目标;`'self'` / `false` 就地渲染。 |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | DOM 透传,见 [样式与 attrs](/docs/attrs). |\n\n\n## Events\n\n| 事件名 | 参数 | 说明 |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | 可见性变化。 |\n| `update:position` | `{ x; y }` | 位置变化。 |\n\n## Methods\n\n| 方法 | 说明 |\n| --- | --- |\n| `show(event)` | 根据鼠标事件或坐标显示。 |\n| `hide()` | 隐藏菜单。 |\n\n`useContextMenu()` 返回 `{ visible, position, show, hide }`,便于命令式打开。\n\n## Slots\n\n| 插槽名 | 说明 |\n| --- | --- |\n| `default` | 触发元素(非 popup 模式)。 |\n\n## 类型\n\n<h4 id=\"ContextMenuItem\">ContextMenuItem</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ninterface ContextMenuItem extends MenuNodeBase {\n items?: ContextMenuItem[]\n}\n```\n"
|
|
4434
|
+
"markdown": "---\ntitle: ContextMenu\ncategory: 04 / NAVIGATION\ndescription: 右键上下文菜单,支持 show(event) / hide()。\n---\n\n# ContextMenu\n\n在指针位置弹出的上下文菜单。支持嵌套 `items`。也可用 `useContextMenu()` 绑定 `v-model` / `v-model:position`。\n\n## 引入\n\n```ts\nimport { useContextMenu, MContextMenu } from 'morya-ui'\n```\n\n## 基础用法\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MContextMenu } from 'morya-ui'\n\nconst model = [\n { label: '复制' },\n { label: '粘贴' },\n { separator: true },\n { label: '删除', disabled: true },\n]\n</script>\n\n<template>\n <MContextMenu :model=\"model\">\n <div\n style=\"border: 1px dashed var(--m-color-border); padding: 2rem; border-radius: var(--m-radius-md)\"\n >\n 右键此处打开菜单\n </div>\n </MContextMenu>\n</template>\n```\n\n## Nested + useContextMenu\n\n```vue preview\n<script setup lang=\"ts\">\nimport { useContextMenu, MContextMenu } from 'morya-ui'\n\nconst menu = useContextMenu()\nconst model = [\n { label: '复制', command: () => undefined },\n { label: '更多', items: [{ label: '深层' }] },\n]\n</script>\n\n<template>\n <div\n style=\"border: 1px dashed var(--m-color-border); padding: 2rem; border-radius: var(--m-radius-md)\"\n @contextmenu=\"menu.show\"\n >\n 右键此处(composable)\n </div>\n <MContextMenu v-model=\"menu.visible\" v-model:position=\"menu.position\" :model=\"model\" />\n</template>\n```\n\n## 滚动与嵌套子菜单\n\n菜单列表与嵌套 `items` 飞出层使用内置 `MScrollbar`(`max-height: min(18rem, 45vh)`)。嵌套层 Teleport 到 `body` 相对父项定位,避免长列表滚动时侧向菜单被裁剪。\n\n## Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `model` | `ContextMenuItem[]` | — | 菜单项,可嵌套 `items`;项可含 `key` / `icon`。 |\n| `modelValue` | `boolean` | `false` | 是否可见。 |\n| `position` | `{ x: number; y: number }` | — | 菜单坐标。 |\n| `teleport` | `boolean` | `true` | 浮层 Teleport;默认挂到 `body`。 |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | 挂载目标;`'self'` / `false` 就地渲染。 |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | DOM 透传,见 [样式与 attrs](/docs/attrs). |\n\n\n## Events\n\n| 事件名 | 参数 | 说明 |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | 可见性变化。 |\n| `update:position` | `{ x; y }` | 位置变化。 |\n\n## Methods\n\n| 方法 | 说明 |\n| --- | --- |\n| `show(event)` | 根据鼠标事件或坐标显示。 |\n| `hide()` | 隐藏菜单。 |\n\n`useContextMenu()` 返回 `{ visible, position, show, hide }`,便于命令式打开。\n\n## Slots\n\n| 插槽名 | 说明 |\n| --- | --- |\n| `default` | 触发元素(非 popup 模式)。 |\n\n## 类型\n\n<h4 id=\"ContextMenuItem\">ContextMenuItem</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ninterface ContextMenuItem extends MenuNodeBase {\n items?: ContextMenuItem[]\n}\n```\n"
|
|
4430
4435
|
},
|
|
4431
4436
|
"en-US": {
|
|
4432
4437
|
"title": "ContextMenu",
|
|
@@ -4452,6 +4457,11 @@
|
|
|
4452
4457
|
"title": "Nested + useContextMenu",
|
|
4453
4458
|
"body": "```vue preview\n<script setup lang=\"ts\">\nimport { useContextMenu, MContextMenu } from 'morya-ui'\n\nconst menu = useContextMenu()\nconst model = [\n { label: 'Copy', command: () => undefined },\n { label: 'More', items: [{ label: 'Nested' }] },\n]\n</script>\n\n<template>\n <div\n style=\"border: 1px dashed var(--m-color-border); padding: 2rem; border-radius: var(--m-radius-md)\"\n @contextmenu=\"menu.show\"\n >\n Right-click here (composable)\n </div>\n <MContextMenu v-model=\"menu.visible\" v-model:position=\"menu.position\" :model=\"model\" />\n</template>\n```"
|
|
4454
4459
|
},
|
|
4460
|
+
{
|
|
4461
|
+
"id": "scrolling-nested-flyouts",
|
|
4462
|
+
"title": "Scrolling & nested flyouts",
|
|
4463
|
+
"body": "The menu list and nested `items` flyouts use built-in `MScrollbar` (`max-height: min(18rem, 45vh)`). Nested panels teleport to `body` and anchor to the parent item so side flyouts are not clipped when the root menu scrolls."
|
|
4464
|
+
},
|
|
4455
4465
|
{
|
|
4456
4466
|
"id": "props",
|
|
4457
4467
|
"title": "Props",
|
|
@@ -4478,7 +4488,7 @@
|
|
|
4478
4488
|
"body": "<h4 id=\"ContextMenuItem\">ContextMenuItem</h4>\n\nSee source `types.ts` for the full definition.\n\n```ts\ninterface ContextMenuItem extends MenuNodeBase {\n items?: ContextMenuItem[]\n}\n```"
|
|
4479
4489
|
}
|
|
4480
4490
|
],
|
|
4481
|
-
"markdown": "---\ntitle: ContextMenu\ncategory: 04 / NAVIGATION\ndescription: Right-click context menu with show(event) / hide().\n---\n\n# ContextMenu\n\nContext menu that opens at the pointer position. Nested `items` are supported. `useContextMenu()` can bind `v-model` / `v-model:position`.\n\n## Import\n\n```ts\nimport { useContextMenu, MContextMenu } from 'morya-ui'\n```\n\n## Basic\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MContextMenu } from 'morya-ui'\n\nconst model = [\n { label: 'Copy' },\n { label: 'Paste' },\n { separator: true },\n { label: 'Delete', disabled: true },\n]\n</script>\n\n<template>\n <MContextMenu :model=\"model\">\n <div\n style=\"border: 1px dashed var(--m-color-border); padding: 2rem; border-radius: var(--m-radius-md)\"\n >\n Right-click here to open the menu\n </div>\n </MContextMenu>\n</template>\n```\n\n## Nested + useContextMenu\n\n```vue preview\n<script setup lang=\"ts\">\nimport { useContextMenu, MContextMenu } from 'morya-ui'\n\nconst menu = useContextMenu()\nconst model = [\n { label: 'Copy', command: () => undefined },\n { label: 'More', items: [{ label: 'Nested' }] },\n]\n</script>\n\n<template>\n <div\n style=\"border: 1px dashed var(--m-color-border); padding: 2rem; border-radius: var(--m-radius-md)\"\n @contextmenu=\"menu.show\"\n >\n Right-click here (composable)\n </div>\n <MContextMenu v-model=\"menu.visible\" v-model:position=\"menu.position\" :model=\"model\" />\n</template>\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `model` | `ContextMenuItem[]` | — | Menu items; may nest `items`. Items may include `key` / `icon`. |\n| `modelValue` | `boolean` | `false` | Whether the menu is visible. |\n| `position` | `{ x: number; y: number }` | — | Menu coordinates. |\n| `teleport` | `boolean` | `true` | Overlay Teleport; mounts to `body` by default. |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Mount target; `'self'` / `false` renders in place. |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | Pass-through; see [Styling & attrs](/docs/attrs). |\n\n\n## Events\n\n| Event | Prop | Description |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | Visibility change. |\n| `update:position` | `{ x; y }` | Position change. |\n\n## Methods\n\n| Method / Property | Description |\n| --- | --- |\n| `show(event)` | Show from a mouse event or coordinates. |\n| `hide()` | Hide the menu. |\n\n`useContextMenu()` returns `{ visible, position, show, hide }` for imperative open.\n\n## Slots\n\n| Slot | Description |\n| --- | --- |\n| `default` | Trigger element (inline mode). |\n\n## Types\n\n<h4 id=\"ContextMenuItem\">ContextMenuItem</h4>\n\nSee source `types.ts` for the full definition.\n\n```ts\ninterface ContextMenuItem extends MenuNodeBase {\n items?: ContextMenuItem[]\n}\n```\n"
|
|
4491
|
+
"markdown": "---\ntitle: ContextMenu\ncategory: 04 / NAVIGATION\ndescription: Right-click context menu with show(event) / hide().\n---\n\n# ContextMenu\n\nContext menu that opens at the pointer position. Nested `items` are supported. `useContextMenu()` can bind `v-model` / `v-model:position`.\n\n## Import\n\n```ts\nimport { useContextMenu, MContextMenu } from 'morya-ui'\n```\n\n## Basic\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MContextMenu } from 'morya-ui'\n\nconst model = [\n { label: 'Copy' },\n { label: 'Paste' },\n { separator: true },\n { label: 'Delete', disabled: true },\n]\n</script>\n\n<template>\n <MContextMenu :model=\"model\">\n <div\n style=\"border: 1px dashed var(--m-color-border); padding: 2rem; border-radius: var(--m-radius-md)\"\n >\n Right-click here to open the menu\n </div>\n </MContextMenu>\n</template>\n```\n\n## Nested + useContextMenu\n\n```vue preview\n<script setup lang=\"ts\">\nimport { useContextMenu, MContextMenu } from 'morya-ui'\n\nconst menu = useContextMenu()\nconst model = [\n { label: 'Copy', command: () => undefined },\n { label: 'More', items: [{ label: 'Nested' }] },\n]\n</script>\n\n<template>\n <div\n style=\"border: 1px dashed var(--m-color-border); padding: 2rem; border-radius: var(--m-radius-md)\"\n @contextmenu=\"menu.show\"\n >\n Right-click here (composable)\n </div>\n <MContextMenu v-model=\"menu.visible\" v-model:position=\"menu.position\" :model=\"model\" />\n</template>\n```\n\n## Scrolling & nested flyouts\n\nThe menu list and nested `items` flyouts use built-in `MScrollbar` (`max-height: min(18rem, 45vh)`). Nested panels teleport to `body` and anchor to the parent item so side flyouts are not clipped when the root menu scrolls.\n\n## Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `model` | `ContextMenuItem[]` | — | Menu items; may nest `items`. Items may include `key` / `icon`. |\n| `modelValue` | `boolean` | `false` | Whether the menu is visible. |\n| `position` | `{ x: number; y: number }` | — | Menu coordinates. |\n| `teleport` | `boolean` | `true` | Overlay Teleport; mounts to `body` by default. |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Mount target; `'self'` / `false` renders in place. |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | Pass-through; see [Styling & attrs](/docs/attrs). |\n\n\n## Events\n\n| Event | Prop | Description |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | Visibility change. |\n| `update:position` | `{ x; y }` | Position change. |\n\n## Methods\n\n| Method / Property | Description |\n| --- | --- |\n| `show(event)` | Show from a mouse event or coordinates. |\n| `hide()` | Hide the menu. |\n\n`useContextMenu()` returns `{ visible, position, show, hide }` for imperative open.\n\n## Slots\n\n| Slot | Description |\n| --- | --- |\n| `default` | Trigger element (inline mode). |\n\n## Types\n\n<h4 id=\"ContextMenuItem\">ContextMenuItem</h4>\n\nSee source `types.ts` for the full definition.\n\n```ts\ninterface ContextMenuItem extends MenuNodeBase {\n items?: ContextMenuItem[]\n}\n```\n"
|
|
4482
4492
|
}
|
|
4483
4493
|
}
|
|
4484
4494
|
},
|
|
@@ -6593,6 +6603,11 @@
|
|
|
6593
6603
|
"title": "Nested / group / hover",
|
|
6594
6604
|
"body": "```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MDropdown } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst items = [\n { type: 'group' as const, label: '编辑', items: [{ value: 'cut', label: '剪切' }, { value: 'copy', label: '复制' }] },\n { separator: true },\n { value: 'more', label: '更多', items: [{ value: 'deep', label: '深层操作' }] },\n]\n</script>\n\n<template>\n <MDropdown v-model=\"open\" :items=\"items\" trigger=\"hover\" :show-delay=\"0\" :hide-delay=\"200\">\n <template #trigger>\n <MButton label=\"悬停打开\" severity=\"secondary\" />\n </template>\n </MDropdown>\n</template>\n```"
|
|
6595
6605
|
},
|
|
6606
|
+
{
|
|
6607
|
+
"id": "滚动与嵌套子菜单",
|
|
6608
|
+
"title": "滚动与嵌套子菜单",
|
|
6609
|
+
"body": "主菜单与嵌套 `items` 的侧向飞出层均内置 `MScrollbar`,高度上限为 `min(18rem, 45vh)`。嵌套子菜单会 **额外 Teleport 到 `body`** 并相对父项定位,避免主菜单滚动时裁剪飞出层;hover 移入飞出层时有短暂延迟关闭。"
|
|
6610
|
+
},
|
|
6596
6611
|
{
|
|
6597
6612
|
"id": "props",
|
|
6598
6613
|
"title": "Props",
|
|
@@ -6614,7 +6629,7 @@
|
|
|
6614
6629
|
"body": "<h4 id=\"DropdownItem\">DropdownItem</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ninterface DropdownItem extends MenuNodeBase {\n type?: DropdownItemType\n items?: DropdownItem[]\n}\n```"
|
|
6615
6630
|
}
|
|
6616
6631
|
],
|
|
6617
|
-
"markdown": "---\ntitle: Dropdown\ncategory: 04 / NAVIGATION\ndescription: 动作菜单覆盖层(非表单选择器)。与 Select 不同:用于触发编辑、删除等操作项。\n---\n\n# Dropdown\n\n动作菜单(action menu overlay)。用于从触发器打开一组操作项。\n\n**与 Select 的区别:** `MDropdown` 是菜单覆盖层;表单选项选择请使用 `MSelect`。\n\n支持分组(`type: 'group'`)、分割线(`separator` / `type: 'divider'`)、嵌套 `items`,以及 `trigger: 'hover'` + `showDelay` / `hideDelay`。键盘高亮仍只覆盖顶层叶子项。\n\n## 引入\n\n```ts\nimport { MButton, MDropdown } from 'morya-ui'\n```\n\n## 基础用法\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MDropdown } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst items = [\n { value: 'edit', label: 'Edit' },\n { value: 'duplicate', label: 'Duplicate' },\n { value: 'delete', label: 'Delete', disabled: true },\n]\n\nfunction onSelect(item: { value: string; label: string }) {\n open.value = false\n console.log(item.value)\n}\n</script>\n\n<template>\n <MDropdown v-model=\"open\" :items=\"items\" @select=\"onSelect\">\n <template #trigger>\n <MButton label=\"Actions\" icon=\"chevron-down\" icon-pos=\"right\" severity=\"secondary\" />\n </template>\n </MDropdown>\n</template>\n```\n\n## Nested / group / hover\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MDropdown } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst items = [\n { type: 'group' as const, label: '编辑', items: [{ value: 'cut', label: '剪切' }, { value: 'copy', label: '复制' }] },\n { separator: true },\n { value: 'more', label: '更多', items: [{ value: 'deep', label: '深层操作' }] },\n]\n</script>\n\n<template>\n <MDropdown v-model=\"open\" :items=\"items\" trigger=\"hover\" :show-delay=\"0\" :hide-delay=\"200\">\n <template #trigger>\n <MButton label=\"悬停打开\" severity=\"secondary\" />\n </template>\n </MDropdown>\n</template>\n```\n\n## Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `modelValue` | `boolean` | `false` | 菜单是否打开。 |\n| `items` | `DropdownItem[]` | — | 菜单项;可含 `type` / `separator` / `items` / `icon` / `command`。 |\n| `placement` | `'bottom-start' \\| 'bottom-end'` | `'bottom-start'` | 对齐方式。 |\n| `closeOnSelect` | `boolean` | `true` | 选择后关闭。 |\n| `trigger` | `'click' \\| 'hover'` | `'click'` | 打开方式。 |\n| `showDelay` / `hideDelay` | `number` | `0` / `200` | hover 延迟(ms)。 |\n| `teleport` | `boolean` | `true` | 将菜单 Teleport 出去;默认挂到 `body`。 |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | 挂载目标;`'self'` / `false` 表示就地渲染。 |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | DOM 透传,见 [样式与 attrs](/docs/attrs). |\n\n\n## Events\n\n| 事件名 | 参数 | 说明 |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | 打开状态变化。 |\n| `select` | `DropdownItem` | 选中启用项时触发。 |\n\n## Slots\n\n| 插槽名 | 说明 |\n| --- | --- |\n| `trigger` | 触发器。 |\n| `item` | 自定义菜单项,作用域 `{ item }`。 |\n\n## 类型\n\n<h4 id=\"DropdownItem\">DropdownItem</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ninterface DropdownItem extends MenuNodeBase {\
|
|
6632
|
+
"markdown": "---\ntitle: Dropdown\ncategory: 04 / NAVIGATION\ndescription: 动作菜单覆盖层(非表单选择器)。与 Select 不同:用于触发编辑、删除等操作项。\n---\n\n# Dropdown\n\n动作菜单(action menu overlay)。用于从触发器打开一组操作项。\n\n**与 Select 的区别:** `MDropdown` 是菜单覆盖层;表单选项选择请使用 `MSelect`。\n\n支持分组(`type: 'group'`)、分割线(`separator` / `type: 'divider'`)、嵌套 `items`,以及 `trigger: 'hover'` + `showDelay` / `hideDelay`。键盘高亮仍只覆盖顶层叶子项。\n\n## 引入\n\n```ts\nimport { MButton, MDropdown } from 'morya-ui'\n```\n\n## 基础用法\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MDropdown } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst items = [\n { value: 'edit', label: 'Edit' },\n { value: 'duplicate', label: 'Duplicate' },\n { value: 'delete', label: 'Delete', disabled: true },\n]\n\nfunction onSelect(item: { value: string; label: string }) {\n open.value = false\n console.log(item.value)\n}\n</script>\n\n<template>\n <MDropdown v-model=\"open\" :items=\"items\" @select=\"onSelect\">\n <template #trigger>\n <MButton label=\"Actions\" icon=\"chevron-down\" icon-pos=\"right\" severity=\"secondary\" />\n </template>\n </MDropdown>\n</template>\n```\n\n## Nested / group / hover\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MDropdown } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst items = [\n { type: 'group' as const, label: '编辑', items: [{ value: 'cut', label: '剪切' }, { value: 'copy', label: '复制' }] },\n { separator: true },\n { value: 'more', label: '更多', items: [{ value: 'deep', label: '深层操作' }] },\n]\n</script>\n\n<template>\n <MDropdown v-model=\"open\" :items=\"items\" trigger=\"hover\" :show-delay=\"0\" :hide-delay=\"200\">\n <template #trigger>\n <MButton label=\"悬停打开\" severity=\"secondary\" />\n </template>\n </MDropdown>\n</template>\n```\n\n## 滚动与嵌套子菜单\n\n主菜单与嵌套 `items` 的侧向飞出层均内置 `MScrollbar`,高度上限为 `min(18rem, 45vh)`。嵌套子菜单会 **额外 Teleport 到 `body`** 并相对父项定位,避免主菜单滚动时裁剪飞出层;hover 移入飞出层时有短暂延迟关闭。\n\n## Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `modelValue` | `boolean` | `false` | 菜单是否打开。 |\n| `items` | `DropdownItem[]` | — | 菜单项;可含 `type` / `separator` / `items` / `icon` / `command`。 |\n| `placement` | `'bottom-start' \\| 'bottom-end'` | `'bottom-start'` | 对齐方式。 |\n| `closeOnSelect` | `boolean` | `true` | 选择后关闭。 |\n| `trigger` | `'click' \\| 'hover'` | `'click'` | 打开方式。 |\n| `showDelay` / `hideDelay` | `number` | `0` / `200` | hover 延迟(ms)。 |\n| `teleport` | `boolean` | `true` | 将菜单 Teleport 出去;默认挂到 `body`。 |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | 挂载目标;`'self'` / `false` 表示就地渲染。 |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | DOM 透传,见 [样式与 attrs](/docs/attrs). |\n\n\n## Events\n\n| 事件名 | 参数 | 说明 |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | 打开状态变化。 |\n| `select` | `DropdownItem` | 选中启用项时触发。 |\n\n## Slots\n\n| 插槽名 | 说明 |\n| --- | --- |\n| `trigger` | 触发器。 |\n| `item` | 自定义菜单项,作用域 `{ item }`。 |\n\n## 类型\n\n<h4 id=\"DropdownItem\">DropdownItem</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ninterface DropdownItem extends MenuNodeBase {\n type?: DropdownItemType\n items?: DropdownItem[]\n}\n```\n"
|
|
6618
6633
|
},
|
|
6619
6634
|
"en-US": {
|
|
6620
6635
|
"title": "Dropdown",
|
|
@@ -6640,6 +6655,11 @@
|
|
|
6640
6655
|
"title": "Nested / group / hover",
|
|
6641
6656
|
"body": "```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MDropdown } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst items = [\n { type: 'group' as const, label: 'Edit', items: [{ value: 'cut', label: 'Cut' }, { value: 'copy', label: 'Copy' }] },\n { separator: true },\n { value: 'more', label: 'More', items: [{ value: 'deep', label: 'Deep action' }] },\n]\n</script>\n\n<template>\n <MDropdown v-model=\"open\" :items=\"items\" trigger=\"hover\" :show-delay=\"0\" :hide-delay=\"200\">\n <template #trigger>\n <MButton label=\"Hover to open\" severity=\"secondary\" />\n </template>\n </MDropdown>\n</template>\n```"
|
|
6642
6657
|
},
|
|
6658
|
+
{
|
|
6659
|
+
"id": "scrolling-nested-flyouts",
|
|
6660
|
+
"title": "Scrolling & nested flyouts",
|
|
6661
|
+
"body": "Both the root menu and nested `items` flyouts use built-in `MScrollbar` with `max-height: min(18rem, 45vh)`. Nested submenus are **also teleported to `body`** and positioned against the parent item so they are not clipped when the root menu scrolls; a short hover delay keeps the flyout open while moving the pointer into it."
|
|
6662
|
+
},
|
|
6643
6663
|
{
|
|
6644
6664
|
"id": "props",
|
|
6645
6665
|
"title": "Props",
|
|
@@ -6661,7 +6681,7 @@
|
|
|
6661
6681
|
"body": "<h4 id=\"DropdownItem\">DropdownItem</h4>\n\nSee source `types.ts` for the full definition.\n\n```ts\ninterface DropdownItem extends MenuNodeBase {\n type?: DropdownItemType\n items?: DropdownItem[]\n}\n```"
|
|
6662
6682
|
}
|
|
6663
6683
|
],
|
|
6664
|
-
"markdown": "---\ntitle: Dropdown\ncategory: 04 / NAVIGATION\ndescription: Action menu overlay (not a form select). Unlike Select, it is used to trigger actions such as edit and delete.\n---\n\n# Dropdown\n\nAction menu overlay. Opens a set of actions from a trigger.\n\n**Difference from Select:** `MDropdown` is a menu overlay; for form option selection, use `MSelect`.\n\nSupports groups (`type: 'group'`), dividers (`separator` / `type: 'divider'`), nested `items`, and `trigger: 'hover'` with `showDelay` / `hideDelay`. Keyboard highlight still covers top-level leaves only.\n\n## Import\n\n```ts\nimport { MButton, MDropdown } from 'morya-ui'\n```\n\n## Basic\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MDropdown } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst items = [\n { value: 'edit', label: 'Edit' },\n { value: 'duplicate', label: 'Duplicate' },\n { value: 'delete', label: 'Delete', disabled: true },\n]\n\nfunction onSelect(item: { value: string; label: string }) {\n open.value = false\n console.log(item.value)\n}\n</script>\n\n<template>\n <MDropdown v-model=\"open\" :items=\"items\" @select=\"onSelect\">\n <template #trigger>\n <MButton label=\"Actions\" icon=\"chevron-down\" icon-pos=\"right\" severity=\"secondary\" />\n </template>\n </MDropdown>\n</template>\n```\n\n## Nested / group / hover\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MDropdown } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst items = [\n { type: 'group' as const, label: 'Edit', items: [{ value: 'cut', label: 'Cut' }, { value: 'copy', label: 'Copy' }] },\n { separator: true },\n { value: 'more', label: 'More', items: [{ value: 'deep', label: 'Deep action' }] },\n]\n</script>\n\n<template>\n <MDropdown v-model=\"open\" :items=\"items\" trigger=\"hover\" :show-delay=\"0\" :hide-delay=\"200\">\n <template #trigger>\n <MButton label=\"Hover to open\" severity=\"secondary\" />\n </template>\n </MDropdown>\n</template>\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `modelValue` | `boolean` | `false` | Whether the menu is open. |\n| `items` | `DropdownItem[]` | — | Menu items; may include `type` / `separator` / `items` / `icon` / `command`. |\n| `placement` | `'bottom-start' \\| 'bottom-end'` | `'bottom-start'` | Alignment. |\n| `closeOnSelect` | `boolean` | `true` | Close after selection. |\n| `trigger` | `'click' \\| 'hover'` | `'click'` | How the menu opens. |\n| `showDelay` / `hideDelay` | `number` | `0` / `200` | Hover delays in ms. |\n| `teleport` | `boolean` | `true` | Teleport the menu; mounts to `body` by default. |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Mount target; `'self'` / `false` renders in place. |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | Pass-through; see [Styling & attrs](/docs/attrs). |\n\n\n## Events\n\n| Event | Prop | Description |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | Open state change. |\n| `select` | `DropdownItem` | Emitted when an enabled item is selected. |\n\n## Slots\n\n| Slot | Description |\n| --- | --- |\n| `trigger` | Trigger. |\n| `item` | Custom menu item, scope `{ item }`. |\n\n## Types\n\n<h4 id=\"DropdownItem\">DropdownItem</h4>\n\nSee source `types.ts` for the full definition.\n\n```ts\ninterface DropdownItem extends MenuNodeBase {\
|
|
6684
|
+
"markdown": "---\ntitle: Dropdown\ncategory: 04 / NAVIGATION\ndescription: Action menu overlay (not a form select). Unlike Select, it is used to trigger actions such as edit and delete.\n---\n\n# Dropdown\n\nAction menu overlay. Opens a set of actions from a trigger.\n\n**Difference from Select:** `MDropdown` is a menu overlay; for form option selection, use `MSelect`.\n\nSupports groups (`type: 'group'`), dividers (`separator` / `type: 'divider'`), nested `items`, and `trigger: 'hover'` with `showDelay` / `hideDelay`. Keyboard highlight still covers top-level leaves only.\n\n## Import\n\n```ts\nimport { MButton, MDropdown } from 'morya-ui'\n```\n\n## Basic\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MDropdown } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst items = [\n { value: 'edit', label: 'Edit' },\n { value: 'duplicate', label: 'Duplicate' },\n { value: 'delete', label: 'Delete', disabled: true },\n]\n\nfunction onSelect(item: { value: string; label: string }) {\n open.value = false\n console.log(item.value)\n}\n</script>\n\n<template>\n <MDropdown v-model=\"open\" :items=\"items\" @select=\"onSelect\">\n <template #trigger>\n <MButton label=\"Actions\" icon=\"chevron-down\" icon-pos=\"right\" severity=\"secondary\" />\n </template>\n </MDropdown>\n</template>\n```\n\n## Nested / group / hover\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MDropdown } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst items = [\n { type: 'group' as const, label: 'Edit', items: [{ value: 'cut', label: 'Cut' }, { value: 'copy', label: 'Copy' }] },\n { separator: true },\n { value: 'more', label: 'More', items: [{ value: 'deep', label: 'Deep action' }] },\n]\n</script>\n\n<template>\n <MDropdown v-model=\"open\" :items=\"items\" trigger=\"hover\" :show-delay=\"0\" :hide-delay=\"200\">\n <template #trigger>\n <MButton label=\"Hover to open\" severity=\"secondary\" />\n </template>\n </MDropdown>\n</template>\n```\n\n## Scrolling & nested flyouts\n\nBoth the root menu and nested `items` flyouts use built-in `MScrollbar` with `max-height: min(18rem, 45vh)`. Nested submenus are **also teleported to `body`** and positioned against the parent item so they are not clipped when the root menu scrolls; a short hover delay keeps the flyout open while moving the pointer into it.\n\n## Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `modelValue` | `boolean` | `false` | Whether the menu is open. |\n| `items` | `DropdownItem[]` | — | Menu items; may include `type` / `separator` / `items` / `icon` / `command`. |\n| `placement` | `'bottom-start' \\| 'bottom-end'` | `'bottom-start'` | Alignment. |\n| `closeOnSelect` | `boolean` | `true` | Close after selection. |\n| `trigger` | `'click' \\| 'hover'` | `'click'` | How the menu opens. |\n| `showDelay` / `hideDelay` | `number` | `0` / `200` | Hover delays in ms. |\n| `teleport` | `boolean` | `true` | Teleport the menu; mounts to `body` by default. |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Mount target; `'self'` / `false` renders in place. |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | Pass-through; see [Styling & attrs](/docs/attrs). |\n\n\n## Events\n\n| Event | Prop | Description |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | Open state change. |\n| `select` | `DropdownItem` | Emitted when an enabled item is selected. |\n\n## Slots\n\n| Slot | Description |\n| --- | --- |\n| `trigger` | Trigger. |\n| `item` | Custom menu item, scope `{ item }`. |\n\n## Types\n\n<h4 id=\"DropdownItem\">DropdownItem</h4>\n\nSee source `types.ts` for the full definition.\n\n```ts\ninterface DropdownItem extends MenuNodeBase {\n type?: DropdownItemType\n items?: DropdownItem[]\n}\n```\n"
|
|
6665
6685
|
}
|
|
6666
6686
|
}
|
|
6667
6687
|
},
|
|
@@ -11728,7 +11748,7 @@
|
|
|
11728
11748
|
{
|
|
11729
11749
|
"id": "scrollable-content",
|
|
11730
11750
|
"title": "Scrollable Content",
|
|
11731
|
-
"body": "内容超出时仅 Content 区域滚动,Header / Sider
|
|
11751
|
+
"body": "内容超出时仅 Content 区域滚动,Header / Sider 保持固定。`MLayout` / `MLayoutContent` / `MLayoutSider` 通过内置 `MScrollbar` 提供统一滚动条。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:14rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Scroll demo\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider bordered :width=\"120\" content-style=\"padding:0.75rem\">\n 固定侧栏\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n <div style=\"display:grid;gap:0.5rem\">\n <div v-for=\"n in 20\" :key=\"n\">\n 行 {{ n }} — 向下滚动\n </div>\n </div>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```"
|
|
11732
11752
|
},
|
|
11733
11753
|
{
|
|
11734
11754
|
"id": "absolute-shell",
|
|
@@ -11743,7 +11763,7 @@
|
|
|
11743
11763
|
{
|
|
11744
11764
|
"id": "layoutsider-props",
|
|
11745
11765
|
"title": "LayoutSider Props",
|
|
11746
|
-
"body": "| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `width` | `number \\| string` | `272` | 展开宽度(始终写在 `width` 上)。 |\n| `collapsedWidth` | `number` | `48` | 折叠时的 `max-width`。 |\n| `collapsed` | `boolean` | — | 折叠状态,支持 `v-model:collapsed`。 |\n| `defaultCollapsed` | `boolean` | `false` | 非受控初始折叠。 |\n| `showTrigger` | `boolean \\| 'bar' \\| 'arrow-circle' \\| 'arrow'` | `false` | 折叠触发器;`arrow` 等同 `arrow-circle`。 |\n| `collapseMode` | `'width' \\| 'transform'` | `'transform'` | `transform` 裁切内容;`width` 随侧栏收缩。 |\n| `showCollapsedContent` | `boolean` | `true` | 折叠后是否仍显示侧栏内容。 |\n| `bordered` / `inverted` | `boolean` | `false` | 边框 / 反色。 |\n| `triggerClass` / `triggerStyle` | — | — | 展开态触发器样式。 |\n| `collapsedTriggerClass` / `collapsedTriggerStyle` | — | — | 折叠态触发器样式。 |"
|
|
11766
|
+
"body": "| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `width` | `number \\| string` | `272` | 展开宽度(始终写在 `width` 上)。 |\n| `collapsedWidth` | `number` | `48` | 折叠时的 `max-width`。 |\n| `collapsed` | `boolean` | — | 折叠状态,支持 `v-model:collapsed`。 |\n| `defaultCollapsed` | `boolean` | `false` | 非受控初始折叠。 |\n| `showTrigger` | `boolean \\| 'bar' \\| 'arrow-circle' \\| 'arrow'` | `false` | 折叠触发器;`arrow` 等同 `arrow-circle`。 |\n| `collapseMode` | `'width' \\| 'transform'` | `'transform'` | `transform` 裁切内容;`width` 随侧栏收缩。 |\n| `showCollapsedContent` | `boolean` | `true` | 折叠后是否仍显示侧栏内容。 |\n| `bordered` / `inverted` | `boolean` | `false` | 边框 / 反色。 |\n| `triggerClass` / `triggerStyle` | — | — | 展开态触发器样式。 |\n| `collapsedTriggerClass` / `collapsedTriggerStyle` | — | — | 折叠态触发器样式。 |\n| `contentClass` / `contentStyle` | — | — | 滚动容器 class / style。 |"
|
|
11747
11767
|
},
|
|
11748
11768
|
{
|
|
11749
11769
|
"id": "events",
|
|
@@ -11766,7 +11786,7 @@
|
|
|
11766
11786
|
"body": "| 插槽名 | 说明 |\n| --- | --- |\n| `default` | 布局区域。 |"
|
|
11767
11787
|
}
|
|
11768
11788
|
],
|
|
11769
|
-
"markdown": "---\ntitle: Layout\ncategory: 06 / LAYOUT\ndescription: 页面级布局骨架,含 Header / Sider / Content / Footer。\n---\n\n# Layout\n\n页面级布局容器。侧栏场景需在对应 `MLayout` 上设置 `has-sider`。给根布局固定高度(或 `min-height`)后,`MLayoutContent` 会自动撑满剩余空间。\n\n## 引入\n\n```ts\nimport {\n MLayout,\n MLayoutContent,\n MLayoutFooter,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n```\n\n## 基础用法\n\nHeader / Content / Footer。Content 会占满中间剩余高度。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutContent, MLayoutFooter, MLayoutHeader } from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:16rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Header\n </MLayoutHeader>\n <MLayoutContent embedded content-style=\"padding:1rem;display:flex;align-items:center;justify-content:center\">\n Content(自动撑开)\n </MLayoutContent>\n <MLayoutFooter bordered style=\"padding:0.75rem 1rem\">\n Footer\n </MLayoutFooter>\n </MLayout>\n</template>\n```\n\n## With Sider\n\n顶栏 + 左侧栏 + 主内容。内层 `has-sider` 的 Layout 会吃掉 Header 以下的全部高度。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\n</script>\n\n<template>\n <MLayout style=\"height:16rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem;display:flex;align-items:center;justify-content:space-between\">\n <strong>App</strong>\n <span style=\"color:var(--m-color-text-muted);font-size:0.75rem\">{{ collapsed ? '已折叠' : '已展开' }}</span>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n :width=\"160\"\n content-style=\"padding:0.75rem\"\n >\n <div style=\"display:grid;gap:0.5rem\">\n <div>概览</div>\n <div>项目</div>\n <div>设置</div>\n </div>\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n 主内容区会横向、纵向同时撑满。\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Right Sider\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:14rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Inspector\n </MLayoutHeader>\n <MLayout has-sider sider-placement=\"right\">\n <MLayoutSider bordered :width=\"140\" content-style=\"padding:0.75rem\">\n 属性面板\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n 画布 / 主区域\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Full Shell\n\n完整后台骨架:顶栏 + 侧栏 + 内容 + 底栏。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MButton,\n MLayout,\n MLayoutContent,\n MLayoutFooter,\n MLayoutHeader,\n MLayoutSider,\n MTag,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\n</script>\n\n<template>\n <MLayout style=\"height:18rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader\n bordered\n inverted\n style=\"padding:0.65rem 1rem;display:flex;align-items:center;gap:0.75rem\"\n >\n <strong>Morya UI</strong>\n <MTag value=\"Studio\" />\n <span style=\"flex:1\" />\n <MButton size=\"small\" label=\"发布\" />\n </MLayoutHeader>\n\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n inverted\n show-trigger=\"bar\"\n :width=\"168\"\n :collapsed-width=\"56\"\n content-style=\"padding:0.75rem\"\n >\n <div style=\"display:grid;gap:0.65rem;font-size:0.875rem\">\n <div>仪表盘</div>\n <div>数据源</div>\n <div>组件</div>\n <div>主题</div>\n </div>\n </MLayoutSider>\n\n <MLayout>\n <MLayoutContent embedded content-style=\"padding:1rem;display:grid;gap:0.75rem;align-content:start\">\n <strong>工作区</strong>\n <p style=\"margin:0;color:var(--m-color-text-muted);font-size:0.875rem\">\n Content 已撑满 Header 与 Footer 之间的空间;侧栏折叠不影响主区高度。\n </p>\n </MLayoutContent>\n <MLayoutFooter bordered style=\"padding:0.5rem 1rem;color:var(--m-color-text-muted);font-size:0.75rem\">\n Ready · local\n </MLayoutFooter>\n </MLayout>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Embedded Content\n\n`embedded` 给内容区柔和背景,便于和顶栏/侧栏区分。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutContent, MLayoutHeader } from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:12rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Settings\n </MLayoutHeader>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n 嵌套表单 / 列表放在这里。\n </MLayoutContent>\n </MLayout>\n</template>\n```\n\n## Scrollable Content\n\n内容超出时仅 Content 区域滚动,Header / Sider
|
|
11789
|
+
"markdown": "---\ntitle: Layout\ncategory: 06 / LAYOUT\ndescription: 页面级布局骨架,含 Header / Sider / Content / Footer。\n---\n\n# Layout\n\n页面级布局容器。侧栏场景需在对应 `MLayout` 上设置 `has-sider`。给根布局固定高度(或 `min-height`)后,`MLayoutContent` 会自动撑满剩余空间。\n\n## 引入\n\n```ts\nimport {\n MLayout,\n MLayoutContent,\n MLayoutFooter,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n```\n\n## 基础用法\n\nHeader / Content / Footer。Content 会占满中间剩余高度。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutContent, MLayoutFooter, MLayoutHeader } from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:16rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Header\n </MLayoutHeader>\n <MLayoutContent embedded content-style=\"padding:1rem;display:flex;align-items:center;justify-content:center\">\n Content(自动撑开)\n </MLayoutContent>\n <MLayoutFooter bordered style=\"padding:0.75rem 1rem\">\n Footer\n </MLayoutFooter>\n </MLayout>\n</template>\n```\n\n## With Sider\n\n顶栏 + 左侧栏 + 主内容。内层 `has-sider` 的 Layout 会吃掉 Header 以下的全部高度。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\n</script>\n\n<template>\n <MLayout style=\"height:16rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem;display:flex;align-items:center;justify-content:space-between\">\n <strong>App</strong>\n <span style=\"color:var(--m-color-text-muted);font-size:0.75rem\">{{ collapsed ? '已折叠' : '已展开' }}</span>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n :width=\"160\"\n content-style=\"padding:0.75rem\"\n >\n <div style=\"display:grid;gap:0.5rem\">\n <div>概览</div>\n <div>项目</div>\n <div>设置</div>\n </div>\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n 主内容区会横向、纵向同时撑满。\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Right Sider\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:14rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Inspector\n </MLayoutHeader>\n <MLayout has-sider sider-placement=\"right\">\n <MLayoutSider bordered :width=\"140\" content-style=\"padding:0.75rem\">\n 属性面板\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n 画布 / 主区域\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Full Shell\n\n完整后台骨架:顶栏 + 侧栏 + 内容 + 底栏。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MButton,\n MLayout,\n MLayoutContent,\n MLayoutFooter,\n MLayoutHeader,\n MLayoutSider,\n MTag,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\n</script>\n\n<template>\n <MLayout style=\"height:18rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader\n bordered\n inverted\n style=\"padding:0.65rem 1rem;display:flex;align-items:center;gap:0.75rem\"\n >\n <strong>Morya UI</strong>\n <MTag value=\"Studio\" />\n <span style=\"flex:1\" />\n <MButton size=\"small\" label=\"发布\" />\n </MLayoutHeader>\n\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n inverted\n show-trigger=\"bar\"\n :width=\"168\"\n :collapsed-width=\"56\"\n content-style=\"padding:0.75rem\"\n >\n <div style=\"display:grid;gap:0.65rem;font-size:0.875rem\">\n <div>仪表盘</div>\n <div>数据源</div>\n <div>组件</div>\n <div>主题</div>\n </div>\n </MLayoutSider>\n\n <MLayout>\n <MLayoutContent embedded content-style=\"padding:1rem;display:grid;gap:0.75rem;align-content:start\">\n <strong>工作区</strong>\n <p style=\"margin:0;color:var(--m-color-text-muted);font-size:0.875rem\">\n Content 已撑满 Header 与 Footer 之间的空间;侧栏折叠不影响主区高度。\n </p>\n </MLayoutContent>\n <MLayoutFooter bordered style=\"padding:0.5rem 1rem;color:var(--m-color-text-muted);font-size:0.75rem\">\n Ready · local\n </MLayoutFooter>\n </MLayout>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Embedded Content\n\n`embedded` 给内容区柔和背景,便于和顶栏/侧栏区分。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutContent, MLayoutHeader } from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:12rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Settings\n </MLayoutHeader>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n 嵌套表单 / 列表放在这里。\n </MLayoutContent>\n </MLayout>\n</template>\n```\n\n## Scrollable Content\n\n内容超出时仅 Content 区域滚动,Header / Sider 保持固定。`MLayout` / `MLayoutContent` / `MLayoutSider` 通过内置 `MScrollbar` 提供统一滚动条。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:14rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Scroll demo\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider bordered :width=\"120\" content-style=\"padding:0.75rem\">\n 固定侧栏\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n <div style=\"display:grid;gap:0.5rem\">\n <div v-for=\"n in 20\" :key=\"n\">\n 行 {{ n }} — 向下滚动\n </div>\n </div>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Absolute Shell\n\n根布局 `position=\"absolute\"` 铺满父级(父级需 `position: relative` + 明确高度)。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n</script>\n\n<template>\n <div style=\"position:relative;height:14rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayout position=\"absolute\" has-sider>\n <MLayoutSider bordered :width=\"120\" content-style=\"padding:0.75rem\">\n Nav\n </MLayoutSider>\n <MLayout>\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Absolute layout\n </MLayoutHeader>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n 填满相对定位容器\n </MLayoutContent>\n </MLayout>\n </MLayout>\n </div>\n</template>\n```\n\n## Layout Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `hasSider` | `boolean` | `false` | 横向容纳 `MLayoutSider`。 |\n| `siderPlacement` | `'left' \\| 'right'` | `'left'` | 侧栏位置。 |\n| `embedded` | `boolean` | `false` | 柔和背景(嵌套内容区)。 |\n| `position` | `'static' \\| 'absolute'` | `'static'` | 定位模式。 |\n| `contentClass` / `contentStyle` | — | — | 滚动容器 class / style。 |\n| `height` | `number \\| string` | — | — |\n| `padding` | `number \\| string` | — | — |\n| `radius` | `number \\| string` | — | — |\n\n## LayoutSider Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `width` | `number \\| string` | `272` | 展开宽度(始终写在 `width` 上)。 |\n| `collapsedWidth` | `number` | `48` | 折叠时的 `max-width`。 |\n| `collapsed` | `boolean` | — | 折叠状态,支持 `v-model:collapsed`。 |\n| `defaultCollapsed` | `boolean` | `false` | 非受控初始折叠。 |\n| `showTrigger` | `boolean \\| 'bar' \\| 'arrow-circle' \\| 'arrow'` | `false` | 折叠触发器;`arrow` 等同 `arrow-circle`。 |\n| `collapseMode` | `'width' \\| 'transform'` | `'transform'` | `transform` 裁切内容;`width` 随侧栏收缩。 |\n| `showCollapsedContent` | `boolean` | `true` | 折叠后是否仍显示侧栏内容。 |\n| `bordered` / `inverted` | `boolean` | `false` | 边框 / 反色。 |\n| `triggerClass` / `triggerStyle` | — | — | 展开态触发器样式。 |\n| `collapsedTriggerClass` / `collapsedTriggerStyle` | — | — | 折叠态触发器样式。 |\n| `contentClass` / `contentStyle` | — | — | 滚动容器 class / style。 |\n\n## Events\n\n| 事件 | 说明 |\n| --- | --- |\n| `scroll` | 滚动容器滚动时触发。 |\n| `after-enter` | 侧栏展开动画结束。 |\n| `after-leave` | 侧栏收起动画结束。 |\n| `collapse` | 侧栏开始收起。 |\n| `expand` | 侧栏开始展开。 |\n| `update:collapsed` | 折叠状态 v-model。 |\n\n## Expose\n\n`MLayout` / `MLayoutContent` / `MLayoutSider` 均暴露 `scrollTo(...)`。\n\n## Components\n\n| 组件 | 说明 |\n| --- | --- |\n| `MLayout` | 根布局。 |\n| `MLayoutHeader` | 顶栏。 |\n| `MLayoutContent` | 主内容区(默认撑满剩余空间)。 |\n| `MLayoutFooter` | 底栏。 |\n| `MLayoutSider` | 侧栏。 |\n\n## Slots\n\n| 插槽名 | 说明 |\n| --- | --- |\n| `default` | 布局区域。 |\n"
|
|
11770
11790
|
},
|
|
11771
11791
|
"en-US": {
|
|
11772
11792
|
"title": "Layout",
|
|
@@ -11810,7 +11830,7 @@
|
|
|
11810
11830
|
{
|
|
11811
11831
|
"id": "scrollable-content",
|
|
11812
11832
|
"title": "Scrollable Content",
|
|
11813
|
-
"body": "Only the content pane scrolls; header and sider stay fixed
|
|
11833
|
+
"body": "Only the content pane scrolls; header and sider stay fixed. `MLayout`, `MLayoutContent`, and `MLayoutSider` use built-in `MScrollbar`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:14rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Scroll demo\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider bordered :width=\"120\" content-style=\"padding:0.75rem\">\n Fixed sider\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n <div style=\"display:grid;gap:0.5rem\">\n <div v-for=\"n in 20\" :key=\"n\">\n Row {{ n }} — scroll down\n </div>\n </div>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```"
|
|
11814
11834
|
},
|
|
11815
11835
|
{
|
|
11816
11836
|
"id": "absolute-shell",
|
|
@@ -11825,7 +11845,7 @@
|
|
|
11825
11845
|
{
|
|
11826
11846
|
"id": "layoutsider-props",
|
|
11827
11847
|
"title": "LayoutSider Props",
|
|
11828
|
-
"body": "| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `width` | `number \\| string` | `272` | Expanded width (always set as `width`). |\n| `collapsedWidth` | `number` | `48` | Collapsed `max-width`. |\n| `collapsed` | `boolean` | — | Collapsed state (`v-model:collapsed`). |\n| `defaultCollapsed` | `boolean` | `false` | Uncontrolled initial collapsed state. |\n| `showTrigger` | `boolean \\| 'bar' \\| 'arrow-circle' \\| 'arrow'` | `false` | Collapse trigger; `arrow` aliases `arrow-circle`. |\n| `collapseMode` | `'width' \\| 'transform'` | `'transform'` | `transform` clips content; `width` shrinks with sider. |\n| `showCollapsedContent` | `boolean` | `true` | Keep sider content visible while collapsed. |\n| `bordered` / `inverted` | `boolean` | `false` | Border / inverted colors. |\n| `triggerClass` / `triggerStyle` | — | — | Expanded trigger styles. |\n| `collapsedTriggerClass` / `collapsedTriggerStyle` | — | — | Collapsed trigger styles. |"
|
|
11848
|
+
"body": "| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `width` | `number \\| string` | `272` | Expanded width (always set as `width`). |\n| `collapsedWidth` | `number` | `48` | Collapsed `max-width`. |\n| `collapsed` | `boolean` | — | Collapsed state (`v-model:collapsed`). |\n| `defaultCollapsed` | `boolean` | `false` | Uncontrolled initial collapsed state. |\n| `showTrigger` | `boolean \\| 'bar' \\| 'arrow-circle' \\| 'arrow'` | `false` | Collapse trigger; `arrow` aliases `arrow-circle`. |\n| `collapseMode` | `'width' \\| 'transform'` | `'transform'` | `transform` clips content; `width` shrinks with sider. |\n| `showCollapsedContent` | `boolean` | `true` | Keep sider content visible while collapsed. |\n| `bordered` / `inverted` | `boolean` | `false` | Border / inverted colors. |\n| `triggerClass` / `triggerStyle` | — | — | Expanded trigger styles. |\n| `collapsedTriggerClass` / `collapsedTriggerStyle` | — | — | Collapsed trigger styles. |\n| `contentClass` / `contentStyle` | — | — | Scroll container class / style. |"
|
|
11829
11849
|
},
|
|
11830
11850
|
{
|
|
11831
11851
|
"id": "events",
|
|
@@ -11848,7 +11868,7 @@
|
|
|
11848
11868
|
"body": "| Slot | Description |\n| --- | --- |\n| `default` | Layout regions. |"
|
|
11849
11869
|
}
|
|
11850
11870
|
],
|
|
11851
|
-
"markdown": "---\ntitle: Layout\ncategory: 06 / LAYOUT\ndescription: Page layout shell with Header / Sider / Content / Footer.\n---\n\n# Layout\n\nPage-level layout shell. Set `has-sider` on the `MLayout` that hosts a sider. Give the root layout a fixed `height` (or `min-height`) so `MLayoutContent` can fill the remaining space.\n\n## Import\n\n```ts\nimport {\n MLayout,\n MLayoutContent,\n MLayoutFooter,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n```\n\n## Basic\n\nHeader / Content / Footer. Content fills the leftover height.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutContent, MLayoutFooter, MLayoutHeader } from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:16rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Header\n </MLayoutHeader>\n <MLayoutContent embedded content-style=\"padding:1rem;display:flex;align-items:center;justify-content:center\">\n Content (fills remaining space)\n </MLayoutContent>\n <MLayoutFooter bordered style=\"padding:0.75rem 1rem\">\n Footer\n </MLayoutFooter>\n </MLayout>\n</template>\n```\n\n## With Sider\n\nHeader + left sider + main. The inner `has-sider` layout consumes all height below the header.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\n</script>\n\n<template>\n <MLayout style=\"height:16rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem;display:flex;align-items:center;justify-content:space-between\">\n <strong>App</strong>\n <span style=\"color:var(--m-color-text-muted);font-size:0.75rem\">{{ collapsed ? 'Collapsed' : 'Expanded' }}</span>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n :width=\"160\"\n content-style=\"padding:0.75rem\"\n >\n <div style=\"display:grid;gap:0.5rem\">\n <div>Overview</div>\n <div>Projects</div>\n <div>Settings</div>\n </div>\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n Main area stretches both horizontally and vertically.\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Right Sider\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:14rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Inspector\n </MLayoutHeader>\n <MLayout has-sider sider-placement=\"right\">\n <MLayoutSider bordered :width=\"140\" content-style=\"padding:0.75rem\">\n Props panel\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n Canvas / main\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Full Shell\n\nAdmin-style shell: header + sider + content + footer.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MButton,\n MLayout,\n MLayoutContent,\n MLayoutFooter,\n MLayoutHeader,\n MLayoutSider,\n MTag,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\n</script>\n\n<template>\n <MLayout style=\"height:18rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader\n bordered\n inverted\n style=\"padding:0.65rem 1rem;display:flex;align-items:center;gap:0.75rem\"\n >\n <strong>Morya UI</strong>\n <MTag value=\"Studio\" />\n <span style=\"flex:1\" />\n <MButton size=\"small\" label=\"Publish\" />\n </MLayoutHeader>\n\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n inverted\n show-trigger=\"bar\"\n :width=\"168\"\n :collapsed-width=\"56\"\n content-style=\"padding:0.75rem\"\n >\n <div style=\"display:grid;gap:0.65rem;font-size:0.875rem\">\n <div>Dashboard</div>\n <div>Datasources</div>\n <div>Widgets</div>\n <div>Theme</div>\n </div>\n </MLayoutSider>\n\n <MLayout>\n <MLayoutContent embedded content-style=\"padding:1rem;display:grid;gap:0.75rem;align-content:start\">\n <strong>Workspace</strong>\n <p style=\"margin:0;color:var(--m-color-text-muted);font-size:0.875rem\">\n Content fills the space between Header and Footer; collapsing the sider keeps the height.\n </p>\n </MLayoutContent>\n <MLayoutFooter bordered style=\"padding:0.5rem 1rem;color:var(--m-color-text-muted);font-size:0.75rem\">\n Ready · local\n </MLayoutFooter>\n </MLayout>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Embedded Content\n\n`embedded` softens the content background so it separates from header / sider.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutContent, MLayoutHeader } from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:12rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Settings\n </MLayoutHeader>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n Nested forms / lists go here.\n </MLayoutContent>\n </MLayout>\n</template>\n```\n\n## Scrollable Content\n\nOnly the content pane scrolls; header and sider stay fixed
|
|
11871
|
+
"markdown": "---\ntitle: Layout\ncategory: 06 / LAYOUT\ndescription: Page layout shell with Header / Sider / Content / Footer.\n---\n\n# Layout\n\nPage-level layout shell. Set `has-sider` on the `MLayout` that hosts a sider. Give the root layout a fixed `height` (or `min-height`) so `MLayoutContent` can fill the remaining space.\n\n## Import\n\n```ts\nimport {\n MLayout,\n MLayoutContent,\n MLayoutFooter,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n```\n\n## Basic\n\nHeader / Content / Footer. Content fills the leftover height.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutContent, MLayoutFooter, MLayoutHeader } from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:16rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Header\n </MLayoutHeader>\n <MLayoutContent embedded content-style=\"padding:1rem;display:flex;align-items:center;justify-content:center\">\n Content (fills remaining space)\n </MLayoutContent>\n <MLayoutFooter bordered style=\"padding:0.75rem 1rem\">\n Footer\n </MLayoutFooter>\n </MLayout>\n</template>\n```\n\n## With Sider\n\nHeader + left sider + main. The inner `has-sider` layout consumes all height below the header.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\n</script>\n\n<template>\n <MLayout style=\"height:16rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem;display:flex;align-items:center;justify-content:space-between\">\n <strong>App</strong>\n <span style=\"color:var(--m-color-text-muted);font-size:0.75rem\">{{ collapsed ? 'Collapsed' : 'Expanded' }}</span>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n :width=\"160\"\n content-style=\"padding:0.75rem\"\n >\n <div style=\"display:grid;gap:0.5rem\">\n <div>Overview</div>\n <div>Projects</div>\n <div>Settings</div>\n </div>\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n Main area stretches both horizontally and vertically.\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Right Sider\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:14rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Inspector\n </MLayoutHeader>\n <MLayout has-sider sider-placement=\"right\">\n <MLayoutSider bordered :width=\"140\" content-style=\"padding:0.75rem\">\n Props panel\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n Canvas / main\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Full Shell\n\nAdmin-style shell: header + sider + content + footer.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MButton,\n MLayout,\n MLayoutContent,\n MLayoutFooter,\n MLayoutHeader,\n MLayoutSider,\n MTag,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\n</script>\n\n<template>\n <MLayout style=\"height:18rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader\n bordered\n inverted\n style=\"padding:0.65rem 1rem;display:flex;align-items:center;gap:0.75rem\"\n >\n <strong>Morya UI</strong>\n <MTag value=\"Studio\" />\n <span style=\"flex:1\" />\n <MButton size=\"small\" label=\"Publish\" />\n </MLayoutHeader>\n\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n inverted\n show-trigger=\"bar\"\n :width=\"168\"\n :collapsed-width=\"56\"\n content-style=\"padding:0.75rem\"\n >\n <div style=\"display:grid;gap:0.65rem;font-size:0.875rem\">\n <div>Dashboard</div>\n <div>Datasources</div>\n <div>Widgets</div>\n <div>Theme</div>\n </div>\n </MLayoutSider>\n\n <MLayout>\n <MLayoutContent embedded content-style=\"padding:1rem;display:grid;gap:0.75rem;align-content:start\">\n <strong>Workspace</strong>\n <p style=\"margin:0;color:var(--m-color-text-muted);font-size:0.875rem\">\n Content fills the space between Header and Footer; collapsing the sider keeps the height.\n </p>\n </MLayoutContent>\n <MLayoutFooter bordered style=\"padding:0.5rem 1rem;color:var(--m-color-text-muted);font-size:0.75rem\">\n Ready · local\n </MLayoutFooter>\n </MLayout>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Embedded Content\n\n`embedded` softens the content background so it separates from header / sider.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutContent, MLayoutHeader } from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:12rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Settings\n </MLayoutHeader>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n Nested forms / lists go here.\n </MLayoutContent>\n </MLayout>\n</template>\n```\n\n## Scrollable Content\n\nOnly the content pane scrolls; header and sider stay fixed. `MLayout`, `MLayoutContent`, and `MLayoutSider` use built-in `MScrollbar`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n</script>\n\n<template>\n <MLayout style=\"height:14rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Scroll demo\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider bordered :width=\"120\" content-style=\"padding:0.75rem\">\n Fixed sider\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n <div style=\"display:grid;gap:0.5rem\">\n <div v-for=\"n in 20\" :key=\"n\">\n Row {{ n }} — scroll down\n </div>\n </div>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Absolute Shell\n\nRoot `position=\"absolute\"` fills a relatively positioned parent with an explicit height.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n} from 'morya-ui'\n</script>\n\n<template>\n <div style=\"position:relative;height:14rem;border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden\">\n <MLayout position=\"absolute\" has-sider>\n <MLayoutSider bordered :width=\"120\" content-style=\"padding:0.75rem\">\n Nav\n </MLayoutSider>\n <MLayout>\n <MLayoutHeader bordered style=\"padding:0.75rem 1rem\">\n Absolute layout\n </MLayoutHeader>\n <MLayoutContent embedded content-style=\"padding:1rem\">\n Fills the relative container\n </MLayoutContent>\n </MLayout>\n </MLayout>\n </div>\n</template>\n```\n\n## Layout Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `hasSider` | `boolean` | `false` | Horizontal layout for `MLayoutSider`. |\n| `siderPlacement` | `'left' \\| 'right'` | `'left'` | Sider side. |\n| `embedded` | `boolean` | `false` | Soft background for nested content. |\n| `position` | `'static' \\| 'absolute'` | `'static'` | Positioning mode. |\n| `contentClass` / `contentStyle` | — | — | Scroll container class / style. |\n\n## LayoutSider Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `width` | `number \\| string` | `272` | Expanded width (always set as `width`). |\n| `collapsedWidth` | `number` | `48` | Collapsed `max-width`. |\n| `collapsed` | `boolean` | — | Collapsed state (`v-model:collapsed`). |\n| `defaultCollapsed` | `boolean` | `false` | Uncontrolled initial collapsed state. |\n| `showTrigger` | `boolean \\| 'bar' \\| 'arrow-circle' \\| 'arrow'` | `false` | Collapse trigger; `arrow` aliases `arrow-circle`. |\n| `collapseMode` | `'width' \\| 'transform'` | `'transform'` | `transform` clips content; `width` shrinks with sider. |\n| `showCollapsedContent` | `boolean` | `true` | Keep sider content visible while collapsed. |\n| `bordered` / `inverted` | `boolean` | `false` | Border / inverted colors. |\n| `triggerClass` / `triggerStyle` | — | — | Expanded trigger styles. |\n| `collapsedTriggerClass` / `collapsedTriggerStyle` | — | — | Collapsed trigger styles. |\n| `contentClass` / `contentStyle` | — | — | Scroll container class / style. |\n\n## Events\n\n| Event | Description |\n| --- | --- |\n| `scroll` | Fired when the scroll container scrolls. |\n\n## Expose\n\n`MLayout` / `MLayoutContent` / `MLayoutSider` expose `scrollTo(...)`.\n\n## Components\n\n| Component | Description |\n| --- | --- |\n| `MLayout` | Root layout. |\n| `MLayoutHeader` | Header bar. |\n| `MLayoutContent` | Main content (fills leftover space by default). |\n| `MLayoutFooter` | Footer bar. |\n| `MLayoutSider` | Side panel. |\n\n## Slots\n\n| Slot | Description |\n| --- | --- |\n| `default` | Layout regions. |\n"
|
|
11852
11872
|
}
|
|
11853
11873
|
}
|
|
11854
11874
|
},
|
|
@@ -12656,7 +12676,7 @@
|
|
|
12656
12676
|
{
|
|
12657
12677
|
"id": "折叠与飞出层",
|
|
12658
12678
|
"title": "折叠与飞出层",
|
|
12659
|
-
"body": "`collapsed` 隐藏文案,仅保留图标;悬停时在右侧显示 `MTooltip` 标签,带子项的节点还会弹出飞出层(`.m-menu--flyout
|
|
12679
|
+
"body": "`collapsed` 隐藏文案,仅保留图标;悬停时在右侧显示 `MTooltip` 标签,带子项的节点还会弹出飞出层(`.m-menu--flyout`)。飞出层经 `MPopover` Teleport 到 `body`,不会被侧栏或 `MLayoutSider` 滚动区域裁剪。`collapsed-width` 应与侧栏折叠宽度一致,用于居中图标。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: '工作区',\n icon: 'folder',\n items: [\n { key: 'home', label: '首页', icon: 'home' },\n { key: 'docs', label: '文档', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: '设置', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n 展开\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n 折叠\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```"
|
|
12660
12680
|
},
|
|
12661
12681
|
{
|
|
12662
12682
|
"id": "嵌入-layout-侧栏",
|
|
@@ -12666,7 +12686,7 @@
|
|
|
12666
12686
|
{
|
|
12667
12687
|
"id": "水平菜单",
|
|
12668
12688
|
"title": "水平菜单",
|
|
12669
|
-
"body": "`mode=\"horizontal\"`
|
|
12689
|
+
"body": "`mode=\"horizontal\"` 用于顶栏一级导航;子菜单经 `MPopover` 以下拉飞出层展示(Teleport + 主题滚动条),选中后自动关闭。`popup` 模式的主菜单列表同样内置 `MScrollbar`。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n { key: 'home', label: '首页', icon: 'home' },\n {\n key: 'products',\n label: '产品',\n icon: 'box',\n items: [\n { key: 'cloud', label: '云服务', icon: 'cloud' },\n { key: 'edge', label: '边缘计算', icon: 'server' },\n ],\n },\n { key: 'about', label: '关于', icon: 'info-circle' },\n]\n</script>\n\n<template>\n <div\n style=\"\n padding: 0 var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" mode=\"horizontal\" embedded />\n </div>\n</template>\n```"
|
|
12670
12690
|
},
|
|
12671
12691
|
{
|
|
12672
12692
|
"id": "反色-深色侧栏",
|
|
@@ -12704,7 +12724,7 @@
|
|
|
12704
12724
|
"body": "<h4 id=\"MenuItem\">MenuItem</h4>\n\n`model` 数组项,支持嵌套:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // 有 vue-router 时用 RouterLink\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\n`key` 未传时用 `label` 作为标识。`separator: true` 时渲染分隔线。更多见 [API 类型](/docs/types)。"
|
|
12705
12725
|
}
|
|
12706
12726
|
],
|
|
12707
|
-
"markdown": "---\ntitle: Menu\ncategory: 04 / NAVIGATION\ndescription: 垂直/水平导航菜单,支持多级嵌套、受控选中、手风琴展开与折叠侧栏飞出层。\n---\n\n# Menu\n\n基于 `model` 渲染的**导航菜单**,适合后台侧栏、顶栏导航等场景。支持:\n\n- 多级嵌套 `items` 与受控 `selectedKey`\n- 展开路径自动跟随选中项;`accordion` 手风琴\n- `collapsed` 图标模式 + 右侧飞出子菜单(Popover)\n- 非 popup 时默认 `embedded`,无边框铺满 `MLayoutSider`\n\n> 单层悬停子菜单见 [TieredMenu](/components/TieredMenu);顶栏菜单见 [Menubar](/components/Menubar);操作列表见 [Dropdown](/components/Dropdown)。\n\n## 引入\n\n```ts\nimport type {MenuItem} from 'morya-ui';\nimport { MMenu } from 'morya-ui'\n```\n\n## 导航选中\n\n为叶子项设置稳定的 `key`,用 `v-model:selected-key` 与路由同步;点击时触发 `select`。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'settings', label: '设置', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>\n```\n\n未提供 `key` 时会回退到 `label`;生产环境建议始终显式设置 `key`。\n\n## 嵌套子菜单\n\n点击带子项的节点可展开/收起;选中子项时父级会显示 `child-active` 高亮。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('reports')\nconst model = [\n {\n key: 'analytics',\n label: '数据分析',\n icon: 'chart-bar',\n items: [\n { key: 'reports', label: '报表', icon: 'file-text' },\n { key: 'monitor', label: '监控', icon: 'activity' },\n ],\n },\n { key: 'settings', label: '系统设置', icon: 'settings' },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 16rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n</template>\n```\n\n## 手风琴与展开控制\n\n`accordion` 同时只保留一个一级子菜单展开。`defaultExpandedKeys` / `v-model:expanded-keys` 可受控展开项;变更 `selectedKey` 时会自动展开其祖先路径。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('a1')\nconst model = [\n { key: 'a', label: '模块 A', items: [{ key: 'a1', label: 'A-1' }] },\n { key: 'b', label: '模块 B', items: [{ key: 'b1', label: 'B-1' }] },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 14rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" accordion embedded />\n </div>\n</template>\n```\n\n## 折叠与飞出层\n\n`collapsed` 隐藏文案,仅保留图标;悬停时在右侧显示 `MTooltip` 标签,带子项的节点还会弹出飞出层(`.m-menu--flyout`)。`collapsed-width` 应与侧栏折叠宽度一致,用于居中图标。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: '工作区',\n icon: 'folder',\n items: [\n { key: 'home', label: '首页', icon: 'home' },\n { key: 'docs', label: '文档', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: '设置', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n 展开\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n 折叠\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```\n\n## 嵌入 Layout 侧栏\n\n推荐结构:**全局 Header + 下方 `has-sider` Layout**。菜单放在 `MLayoutSider` 内,与 `v-model:collapsed` 联动。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n MMenu,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n {\n key: 'system',\n label: '系统',\n icon: 'settings',\n items: [\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'roles', label: '角色', icon: 'shield' },\n ],\n },\n]\n</script>\n\n<template>\n <MLayout\n style=\"\n height: 14rem;\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutHeader\n bordered\n style=\"\n padding: 0 var(--m-space-4);\n display: flex;\n align-items: center;\n min-height: var(--m-layout-header-height);\n \"\n >\n <strong style=\"color: var(--m-color-primary); font-size: var(--m-font-size-md)\">头部菜单</strong>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n collapse-mode=\"width\"\n :collapsed-width=\"120\"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"64\"\n />\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding: var(--m-space-4)\">\n <p style=\"margin: 0; color: var(--m-color-text-muted); font-size: var(--m-font-size-sm)\">\n 当前选中:<strong style=\"color: var(--m-color-text)\">{{ selectedKey }}</strong>\n </p>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## 水平菜单\n\n`mode=\"horizontal\"` 用于顶栏一级导航;子菜单以下拉飞出层展示,选中后自动关闭。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n { key: 'home', label: '首页', icon: 'home' },\n {\n key: 'products',\n label: '产品',\n icon: 'box',\n items: [\n { key: 'cloud', label: '云服务', icon: 'cloud' },\n { key: 'edge', label: '边缘计算', icon: 'server' },\n ],\n },\n { key: 'about', label: '关于', icon: 'info-circle' },\n]\n</script>\n\n<template>\n <div\n style=\"\n padding: 0 var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" mode=\"horizontal\" embedded />\n </div>\n</template>\n```\n\n## 反色(深色侧栏)\n\n`inverted` 配合 `MLayoutSider` 的 `inverted`,用于深色背景侧栏。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutSider, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n]\n</script>\n\n<template>\n <MLayout\n has-sider\n style=\"\n height: 10rem;\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutSider inverted bordered style=\"width: 12rem\">\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" inverted />\n </MLayoutSider>\n </MLayout>\n</template>\n```\n\n## 弹出模式\n\n`popup` + `v-model` 将菜单作为浮层,默认 Teleport 到 `body` 并相对**默认插槽触发器**定位(无插槽时回退到最后一次指针位置)。点击外部或选中叶子项后关闭。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [\n { label: '复制', command: () => undefined },\n { separator: true },\n { label: '删除', disabled: true },\n]\n</script>\n\n<template>\n <MMenu v-model=\"open\" popup :model=\"model\">\n <MButton label=\"更多操作\" @click=\"open = !open\" />\n </MMenu>\n</template>\n```\n\n## Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `model` | `MenuItem[]` | — | 菜单项,可嵌套 `items`。 |\n| `popup` | `boolean` | `false` | 浮层模式;配合 `v-model` 控制显隐。 |\n| `modelValue` | `boolean` | `false` | popup 可见性(`v-model`)。 |\n| `placement` | `'bottom-start' \\| 'bottom-end' \\| 'top-start' \\| 'top-end'` | `'bottom-start'` | popup 相对触发器的位置。 |\n| `selectedKey` | `string \\| null` | `null` | 当前选中项 key(`v-model:selected-key`)。 |\n| `collapsed` | `boolean` | `false` | 图标模式;子菜单以右侧飞出层展示。 |\n| `collapsedWidth` | `number` | `80` | 折叠宽度(px),用于居中图标。 |\n| `indent` | `number` | `12` | 每层额外左内边距(px)。 |\n| `rootIndent` | `number` | `16` | 根级左内边距(px)。 |\n| `accordion` | `boolean` | `false` | 手风琴:同时只展开一个一级子菜单。 |\n| `defaultExpandedKeys` | `string[]` | `[]` | 默认展开的 submenu keys。 |\n| `expandedKeys` | `string[]` | — | 受控展开 keys(`v-model:expanded-keys`)。 |\n| `defaultExpandAll` | `boolean` | `false` | 初始展开全部子菜单。 |\n| `mode` | `'vertical' \\| 'horizontal'` | `'vertical'` | 布局方向。 |\n| `inverted` | `boolean` | `false` | 反色样式,适合深色侧栏。 |\n| `embedded` | `boolean` | `!popup` | 嵌入布局:去边框与最小宽度。 |\n| `teleport` | `boolean` | `true` | popup 时 Teleport 到 `appendTo`。 |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Teleport 目标;未传时使用 ConfigProvider。 |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | DOM 透传,见 [样式与 attrs](/docs/attrs). |\n\n\n## Events\n\n| 事件名 | 参数 | 说明 |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | popup 可见性变化。 |\n| `update:selectedKey` | `string \\| null` | 选中项变化。 |\n| `update:expandedKeys` | `string[]` | 展开项变化。 |\n| `select` | `MenuItem` | 点击叶子项(非 disabled / separator)。 |\n\n## Slots\n\n| 插槽 | 说明 |\n| --- | --- |\n| `default` | popup 模式的触发器锚点(如按钮);菜单相对其定位。 |\n\n## MenuItem\n\n| 字段 | 类型 | 说明 |\n| --- | --- | --- |\n| `key` | `string` | 唯一标识;未传时使用 `label`。 |\n| `label` | `string` | 展示文本。 |\n| `icon` | `string` | [Tabler 图标名](/components/Icon) 或字符。 |\n| `command` | `() => void` | 点击回调(与 `select` 事件同时触发)。 |\n| `disabled` | `boolean` | 禁用。 |\n| `separator` | `boolean` | 分隔线(忽略其他字段)。 |\n| `items` | `MenuItem[]` | 子菜单。 |\n\n## 类型\n\n<h4 id=\"MenuItem\">MenuItem</h4>\n\n`model` 数组项,支持嵌套:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // 有 vue-router 时用 RouterLink\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\n`key` 未传时用 `label` 作为标识。`separator: true` 时渲染分隔线。更多见 [API 类型](/docs/types)。\n"
|
|
12727
|
+
"markdown": "---\ntitle: Menu\ncategory: 04 / NAVIGATION\ndescription: 垂直/水平导航菜单,支持多级嵌套、受控选中、手风琴展开与折叠侧栏飞出层。\n---\n\n# Menu\n\n基于 `model` 渲染的**导航菜单**,适合后台侧栏、顶栏导航等场景。支持:\n\n- 多级嵌套 `items` 与受控 `selectedKey`\n- 展开路径自动跟随选中项;`accordion` 手风琴\n- `collapsed` 图标模式 + 右侧飞出子菜单(Popover)\n- 非 popup 时默认 `embedded`,无边框铺满 `MLayoutSider`\n\n> 单层悬停子菜单见 [TieredMenu](/components/TieredMenu);顶栏菜单见 [Menubar](/components/Menubar);操作列表见 [Dropdown](/components/Dropdown)。\n\n## 引入\n\n```ts\nimport type {MenuItem} from 'morya-ui';\nimport { MMenu } from 'morya-ui'\n```\n\n## 导航选中\n\n为叶子项设置稳定的 `key`,用 `v-model:selected-key` 与路由同步;点击时触发 `select`。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'settings', label: '设置', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>\n```\n\n未提供 `key` 时会回退到 `label`;生产环境建议始终显式设置 `key`。\n\n## 嵌套子菜单\n\n点击带子项的节点可展开/收起;选中子项时父级会显示 `child-active` 高亮。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('reports')\nconst model = [\n {\n key: 'analytics',\n label: '数据分析',\n icon: 'chart-bar',\n items: [\n { key: 'reports', label: '报表', icon: 'file-text' },\n { key: 'monitor', label: '监控', icon: 'activity' },\n ],\n },\n { key: 'settings', label: '系统设置', icon: 'settings' },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 16rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n</template>\n```\n\n## 手风琴与展开控制\n\n`accordion` 同时只保留一个一级子菜单展开。`defaultExpandedKeys` / `v-model:expanded-keys` 可受控展开项;变更 `selectedKey` 时会自动展开其祖先路径。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('a1')\nconst model = [\n { key: 'a', label: '模块 A', items: [{ key: 'a1', label: 'A-1' }] },\n { key: 'b', label: '模块 B', items: [{ key: 'b1', label: 'B-1' }] },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 14rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" accordion embedded />\n </div>\n</template>\n```\n\n## 折叠与飞出层\n\n`collapsed` 隐藏文案,仅保留图标;悬停时在右侧显示 `MTooltip` 标签,带子项的节点还会弹出飞出层(`.m-menu--flyout`)。飞出层经 `MPopover` Teleport 到 `body`,不会被侧栏或 `MLayoutSider` 滚动区域裁剪。`collapsed-width` 应与侧栏折叠宽度一致,用于居中图标。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: '工作区',\n icon: 'folder',\n items: [\n { key: 'home', label: '首页', icon: 'home' },\n { key: 'docs', label: '文档', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: '设置', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n 展开\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n 折叠\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```\n\n## 嵌入 Layout 侧栏\n\n推荐结构:**全局 Header + 下方 `has-sider` Layout**。菜单放在 `MLayoutSider` 内,与 `v-model:collapsed` 联动。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n MMenu,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n {\n key: 'system',\n label: '系统',\n icon: 'settings',\n items: [\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'roles', label: '角色', icon: 'shield' },\n ],\n },\n]\n</script>\n\n<template>\n <MLayout\n style=\"\n height: 14rem;\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutHeader\n bordered\n style=\"\n padding: 0 var(--m-space-4);\n display: flex;\n align-items: center;\n min-height: var(--m-layout-header-height);\n \"\n >\n <strong style=\"color: var(--m-color-primary); font-size: var(--m-font-size-md)\">头部菜单</strong>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n collapse-mode=\"width\"\n :collapsed-width=\"120\"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"64\"\n />\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding: var(--m-space-4)\">\n <p style=\"margin: 0; color: var(--m-color-text-muted); font-size: var(--m-font-size-sm)\">\n 当前选中:<strong style=\"color: var(--m-color-text)\">{{ selectedKey }}</strong>\n </p>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## 水平菜单\n\n`mode=\"horizontal\"` 用于顶栏一级导航;子菜单经 `MPopover` 以下拉飞出层展示(Teleport + 主题滚动条),选中后自动关闭。`popup` 模式的主菜单列表同样内置 `MScrollbar`。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n { key: 'home', label: '首页', icon: 'home' },\n {\n key: 'products',\n label: '产品',\n icon: 'box',\n items: [\n { key: 'cloud', label: '云服务', icon: 'cloud' },\n { key: 'edge', label: '边缘计算', icon: 'server' },\n ],\n },\n { key: 'about', label: '关于', icon: 'info-circle' },\n]\n</script>\n\n<template>\n <div\n style=\"\n padding: 0 var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" mode=\"horizontal\" embedded />\n </div>\n</template>\n```\n\n## 反色(深色侧栏)\n\n`inverted` 配合 `MLayoutSider` 的 `inverted`,用于深色背景侧栏。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutSider, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n]\n</script>\n\n<template>\n <MLayout\n has-sider\n style=\"\n height: 10rem;\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutSider inverted bordered style=\"width: 12rem\">\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" inverted />\n </MLayoutSider>\n </MLayout>\n</template>\n```\n\n## 弹出模式\n\n`popup` + `v-model` 将菜单作为浮层,默认 Teleport 到 `body` 并相对**默认插槽触发器**定位(无插槽时回退到最后一次指针位置)。点击外部或选中叶子项后关闭。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [\n { label: '复制', command: () => undefined },\n { separator: true },\n { label: '删除', disabled: true },\n]\n</script>\n\n<template>\n <MMenu v-model=\"open\" popup :model=\"model\">\n <MButton label=\"更多操作\" @click=\"open = !open\" />\n </MMenu>\n</template>\n```\n\n## Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `model` | `MenuItem[]` | — | 菜单项,可嵌套 `items`。 |\n| `popup` | `boolean` | `false` | 浮层模式;配合 `v-model` 控制显隐。 |\n| `modelValue` | `boolean` | `false` | popup 可见性(`v-model`)。 |\n| `placement` | `'bottom-start' \\| 'bottom-end' \\| 'top-start' \\| 'top-end'` | `'bottom-start'` | popup 相对触发器的位置。 |\n| `selectedKey` | `string \\| null` | `null` | 当前选中项 key(`v-model:selected-key`)。 |\n| `collapsed` | `boolean` | `false` | 图标模式;子菜单以右侧飞出层展示。 |\n| `collapsedWidth` | `number` | `80` | 折叠宽度(px),用于居中图标。 |\n| `indent` | `number` | `12` | 每层额外左内边距(px)。 |\n| `rootIndent` | `number` | `16` | 根级左内边距(px)。 |\n| `accordion` | `boolean` | `false` | 手风琴:同时只展开一个一级子菜单。 |\n| `defaultExpandedKeys` | `string[]` | `[]` | 默认展开的 submenu keys。 |\n| `expandedKeys` | `string[]` | — | 受控展开 keys(`v-model:expanded-keys`)。 |\n| `defaultExpandAll` | `boolean` | `false` | 初始展开全部子菜单。 |\n| `mode` | `'vertical' \\| 'horizontal'` | `'vertical'` | 布局方向。 |\n| `inverted` | `boolean` | `false` | 反色样式,适合深色侧栏。 |\n| `embedded` | `boolean` | `!popup` | 嵌入布局:去边框与最小宽度。 |\n| `teleport` | `boolean` | `true` | popup 时 Teleport 到 `appendTo`。 |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Teleport 目标;未传时使用 ConfigProvider。 |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | DOM 透传,见 [样式与 attrs](/docs/attrs). |\n\n\n## Events\n\n| 事件名 | 参数 | 说明 |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | popup 可见性变化。 |\n| `update:selectedKey` | `string \\| null` | 选中项变化。 |\n| `update:expandedKeys` | `string[]` | 展开项变化。 |\n| `select` | `MenuItem` | 点击叶子项(非 disabled / separator)。 |\n\n## Slots\n\n| 插槽 | 说明 |\n| --- | --- |\n| `default` | popup 模式的触发器锚点(如按钮);菜单相对其定位。 |\n\n## MenuItem\n\n| 字段 | 类型 | 说明 |\n| --- | --- | --- |\n| `key` | `string` | 唯一标识;未传时使用 `label`。 |\n| `label` | `string` | 展示文本。 |\n| `icon` | `string` | [Tabler 图标名](/components/Icon) 或字符。 |\n| `command` | `() => void` | 点击回调(与 `select` 事件同时触发)。 |\n| `disabled` | `boolean` | 禁用。 |\n| `separator` | `boolean` | 分隔线(忽略其他字段)。 |\n| `items` | `MenuItem[]` | 子菜单。 |\n\n## 类型\n\n<h4 id=\"MenuItem\">MenuItem</h4>\n\n`model` 数组项,支持嵌套:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // 有 vue-router 时用 RouterLink\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\n`key` 未传时用 `label` 作为标识。`separator: true` 时渲染分隔线。更多见 [API 类型](/docs/types)。\n"
|
|
12708
12728
|
},
|
|
12709
12729
|
"en-US": {
|
|
12710
12730
|
"title": "Menu",
|
|
@@ -12738,7 +12758,7 @@
|
|
|
12738
12758
|
{
|
|
12739
12759
|
"id": "collapsed-flyout",
|
|
12740
12760
|
"title": "Collapsed & flyout",
|
|
12741
|
-
"body": "`collapsed` hides labels and keeps icons. Hover shows an `MTooltip` with the label; groups also open a right flyout (`.m-menu--flyout`). Set `collapsed-width` to match the sider width for centered icons.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: 'Workspace',\n icon: 'folder',\n items: [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'docs', label: 'Docs', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n Expanded\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n Collapsed\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```"
|
|
12761
|
+
"body": "`collapsed` hides labels and keeps icons. Hover shows an `MTooltip` with the label; groups also open a right flyout (`.m-menu--flyout`) via `MPopover` teleported to `body`, so it is not clipped by sider scroll regions. Set `collapsed-width` to match the sider width for centered icons.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: 'Workspace',\n icon: 'folder',\n items: [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'docs', label: 'Docs', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n Expanded\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n Collapsed\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```"
|
|
12742
12762
|
},
|
|
12743
12763
|
{
|
|
12744
12764
|
"id": "embed-in-layout-sider",
|
|
@@ -12748,7 +12768,7 @@
|
|
|
12748
12768
|
{
|
|
12749
12769
|
"id": "horizontal-mode",
|
|
12750
12770
|
"title": "Horizontal mode",
|
|
12751
|
-
"body": "`mode=\"horizontal\"` for top nav bars. Submenus open in a dropdown flyout and close after selection
|
|
12771
|
+
"body": "`mode=\"horizontal\"` for top nav bars. Submenus open in a `MPopover` dropdown flyout (teleported, themed scrollbar) and close after selection. Popup mode root menus also use built-in `MScrollbar`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n { key: 'home', label: 'Home', icon: 'home' },\n {\n key: 'products',\n label: 'Products',\n icon: 'box',\n items: [\n { key: 'cloud', label: 'Cloud', icon: 'cloud' },\n { key: 'edge', label: 'Edge', icon: 'server' },\n ],\n },\n { key: 'about', label: 'About', icon: 'info-circle' },\n]\n</script>\n\n<template>\n <div\n style=\"\n padding: 0 var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" mode=\"horizontal\" embedded />\n </div>\n</template>\n```"
|
|
12752
12772
|
},
|
|
12753
12773
|
{
|
|
12754
12774
|
"id": "inverted-dark-sider",
|
|
@@ -12786,7 +12806,7 @@
|
|
|
12786
12806
|
"body": "<h4 id=\"MenuItem\">MenuItem</h4>\n\nEach entry in `model`; supports nesting:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // RouterLink when vue-router is installed\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\nWhen `key` is omitted, `label` is used as the identifier. `separator: true` renders a divider. See also [API types](/docs/types)."
|
|
12787
12807
|
}
|
|
12788
12808
|
],
|
|
12789
|
-
"markdown": "---\ntitle: Menu\ncategory: 04 / NAVIGATION\ndescription: Vertical/horizontal navigation menu with nested items, controlled selection, accordion, and collapsed flyout submenus.\n---\n\n# Menu\n\nNavigation menu rendered from a `model`. Typical uses: admin sidebar, top navigation.\n\n- Nested `items` with controlled `selectedKey`\n- Auto-expand ancestor path when selection changes; optional `accordion`\n- `collapsed` icon rail with right-side flyout submenus (Popover)\n- Non-popup menus default to `embedded` (borderless, full-width in `MLayoutSider`)\n\n> One-level hover submenus: [TieredMenu](/components/TieredMenu). Top bar: [Menubar](/components/Menubar). Action lists: [Dropdown](/components/Dropdown).\n\n## Import\n\n```ts\nimport type {MenuItem} from 'morya-ui';\nimport { MMenu } from 'morya-ui'\n```\n\n## Selection\n\nGive leaf items stable `key` values; sync with routing via `v-model:selected-key`. Emits `select` on click.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n { key: 'users', label: 'Users', icon: 'users' },\n { key: 'settings', label: 'Settings', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 15rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>\n```\n\nIf `key` is omitted, `label` is used as fallback. Prefer explicit keys in production.\n\n## Nested submenus\n\nClick a group to expand/collapse. When a child is selected, the parent shows `child-active` styling.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('reports')\nconst model = [\n {\n key: 'analytics',\n label: 'Analytics',\n icon: 'chart-bar',\n items: [\n { key: 'reports', label: 'Reports', icon: 'file-text' },\n { key: 'monitor', label: 'Monitor', icon: 'activity' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 16rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n</template>\n```\n\n## Accordion & expanded keys\n\n`accordion` keeps at most one top-level group open. Use `defaultExpandedKeys` or `v-model:expanded-keys` for controlled expansion. Changing `selectedKey` auto-expands its ancestor path.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('a1')\nconst model = [\n { key: 'a', label: 'Module A', items: [{ key: 'a1', label: 'A-1' }] },\n { key: 'b', label: 'Module B', items: [{ key: 'b1', label: 'B-1' }] },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 14rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" accordion embedded />\n </div>\n</template>\n```\n\n## Collapsed & flyout\n\n`collapsed` hides labels and keeps icons. Hover shows an `MTooltip` with the label; groups also open a right flyout (`.m-menu--flyout`). Set `collapsed-width` to match the sider width for centered icons.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: 'Workspace',\n icon: 'folder',\n items: [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'docs', label: 'Docs', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n Expanded\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n Collapsed\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```\n\n## Embed in Layout sider\n\nRecommended shell: **global Header + inner `has-sider` Layout**. Bind menu `collapsed` to `MLayoutSider`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n MMenu,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n {\n key: 'system',\n label: 'System',\n icon: 'settings',\n items: [\n { key: 'users', label: 'Users', icon: 'users' },\n { key: 'roles', label: 'Roles', icon: 'shield' },\n ],\n },\n]\n</script>\n\n<template>\n <MLayout\n style=\"\n height: 14rem;\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutHeader\n bordered\n style=\"\n padding: 0 var(--m-space-4);\n display: flex;\n align-items: center;\n min-height: var(--m-layout-header-height);\n \"\n >\n <strong style=\"color: var(--m-color-primary); font-size: var(--m-font-size-md)\">Morya UI</strong>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n collapse-mode=\"width\"\n :collapsed-width=\"64\"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"64\"\n accordion\n />\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding: var(--m-space-4)\">\n <p style=\"margin: 0; color: var(--m-color-text-muted); font-size: var(--m-font-size-sm)\">\n Selected: <strong style=\"color: var(--m-color-text)\">{{ selectedKey }}</strong>\n </p>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Horizontal mode\n\n`mode=\"horizontal\"` for top nav bars. Submenus open in a dropdown flyout and close after selection.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n { key: 'home', label: 'Home', icon: 'home' },\n {\n key: 'products',\n label: 'Products',\n icon: 'box',\n items: [\n { key: 'cloud', label: 'Cloud', icon: 'cloud' },\n { key: 'edge', label: 'Edge', icon: 'server' },\n ],\n },\n { key: 'about', label: 'About', icon: 'info-circle' },\n]\n</script>\n\n<template>\n <div\n style=\"\n padding: 0 var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" mode=\"horizontal\" embedded />\n </div>\n</template>\n```\n\n## Inverted (dark sider)\n\nUse `inverted` with `MLayoutSider`'s `inverted` on dark backgrounds.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutSider, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n { key: 'users', label: 'Users', icon: 'users' },\n]\n</script>\n\n<template>\n <MLayout\n has-sider\n style=\"\n height: 10rem;\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutSider inverted bordered style=\"width: 12rem\">\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" inverted />\n </MLayoutSider>\n </MLayout>\n</template>\n```\n\n## Popup mode\n\n`popup` + `v-model` renders a floating menu, teleported to `body` by default and positioned against the **default-slot trigger** (falls back to the last pointer position when no slot). Closes on outside click or leaf selection.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [\n { label: 'Copy', command: () => undefined },\n { separator: true },\n { label: 'Delete', disabled: true },\n]\n</script>\n\n<template>\n <MMenu v-model=\"open\" popup :model=\"model\">\n <MButton label=\"More\" @click=\"open = !open\" />\n </MMenu>\n</template>\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `model` | `MenuItem[]` | — | Menu items; may nest `items`. |\n| `popup` | `boolean` | `false` | Overlay mode; use with `v-model`. |\n| `modelValue` | `boolean` | `false` | Popup visibility (`v-model`). |\n| `placement` | `'bottom-start' \\| 'bottom-end' \\| 'top-start' \\| 'top-end'` | `'bottom-start'` | Popup position relative to the trigger. |\n| `selectedKey` | `string \\| null` | `null` | Selected item key (`v-model:selected-key`). |\n| `collapsed` | `boolean` | `false` | Icon-only; submenus in right flyout. |\n| `collapsedWidth` | `number` | `80` | Collapsed width (px) for icon centering. |\n| `indent` | `number` | `12` | Extra padding-left per level (px). |\n| `rootIndent` | `number` | `16` | Root item padding-left (px). |\n| `accordion` | `boolean` | `false` | Only one top-level submenu open at a time. |\n| `defaultExpandedKeys` | `string[]` | `[]` | Initially expanded submenu keys. |\n| `expandedKeys` | `string[]` | — | Controlled expanded keys (`v-model:expanded-keys`). |\n| `defaultExpandAll` | `boolean` | `false` | Expand all submenus initially. |\n| `mode` | `'vertical' \\| 'horizontal'` | `'vertical'` | Layout direction. |\n| `inverted` | `boolean` | `false` | Inverted colors for dark sider. |\n| `embedded` | `boolean` | `!popup` | Embed in layout (no border/min-width). |\n| `teleport` | `boolean` | `true` | Teleport popup to `appendTo`. |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Teleport target; falls back to ConfigProvider. |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | Pass-through; see [Styling & attrs](/docs/attrs). |\n\n\n## Events\n\n| Event | Payload | Description |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | Popup visibility changed. |\n| `update:selectedKey` | `string \\| null` | Selected item changed. |\n| `update:expandedKeys` | `string[]` | Expanded keys changed. |\n| `select` | `MenuItem` | Leaf clicked (not disabled / separator). |\n\n## Slots\n\n| Slot | Description |\n| --- | --- |\n| `default` | Popup trigger anchor (e.g. a button); menu positions against it. |\n\n## MenuItem\n\n| Field | Type | Description |\n| --- | --- | --- |\n| `key` | `string` | Unique id; falls back to `label`. |\n| `label` | `string` | Display text. |\n| `icon` | `string` | [Tabler icon name](/components/Icon) or character. |\n| `command` | `() => void` | Click handler (also emits `select`). |\n| `disabled` | `boolean` | Disabled state. |\n| `separator` | `boolean` | Separator line (ignores other fields). |\n| `items` | `MenuItem[]` | Child menu items. |\n\n## Types\n\n<h4 id=\"MenuItem\">MenuItem</h4>\n\nEach entry in `model`; supports nesting:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // RouterLink when vue-router is installed\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\nWhen `key` is omitted, `label` is used as the identifier. `separator: true` renders a divider. See also [API types](/docs/types).\n"
|
|
12809
|
+
"markdown": "---\ntitle: Menu\ncategory: 04 / NAVIGATION\ndescription: Vertical/horizontal navigation menu with nested items, controlled selection, accordion, and collapsed flyout submenus.\n---\n\n# Menu\n\nNavigation menu rendered from a `model`. Typical uses: admin sidebar, top navigation.\n\n- Nested `items` with controlled `selectedKey`\n- Auto-expand ancestor path when selection changes; optional `accordion`\n- `collapsed` icon rail with right-side flyout submenus (Popover)\n- Non-popup menus default to `embedded` (borderless, full-width in `MLayoutSider`)\n\n> One-level hover submenus: [TieredMenu](/components/TieredMenu). Top bar: [Menubar](/components/Menubar). Action lists: [Dropdown](/components/Dropdown).\n\n## Import\n\n```ts\nimport type {MenuItem} from 'morya-ui';\nimport { MMenu } from 'morya-ui'\n```\n\n## Selection\n\nGive leaf items stable `key` values; sync with routing via `v-model:selected-key`. Emits `select` on click.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n { key: 'users', label: 'Users', icon: 'users' },\n { key: 'settings', label: 'Settings', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 15rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>\n```\n\nIf `key` is omitted, `label` is used as fallback. Prefer explicit keys in production.\n\n## Nested submenus\n\nClick a group to expand/collapse. When a child is selected, the parent shows `child-active` styling.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('reports')\nconst model = [\n {\n key: 'analytics',\n label: 'Analytics',\n icon: 'chart-bar',\n items: [\n { key: 'reports', label: 'Reports', icon: 'file-text' },\n { key: 'monitor', label: 'Monitor', icon: 'activity' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 16rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n</template>\n```\n\n## Accordion & expanded keys\n\n`accordion` keeps at most one top-level group open. Use `defaultExpandedKeys` or `v-model:expanded-keys` for controlled expansion. Changing `selectedKey` auto-expands its ancestor path.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('a1')\nconst model = [\n { key: 'a', label: 'Module A', items: [{ key: 'a1', label: 'A-1' }] },\n { key: 'b', label: 'Module B', items: [{ key: 'b1', label: 'B-1' }] },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 14rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" accordion embedded />\n </div>\n</template>\n```\n\n## Collapsed & flyout\n\n`collapsed` hides labels and keeps icons. Hover shows an `MTooltip` with the label; groups also open a right flyout (`.m-menu--flyout`) via `MPopover` teleported to `body`, so it is not clipped by sider scroll regions. Set `collapsed-width` to match the sider width for centered icons.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: 'Workspace',\n icon: 'folder',\n items: [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'docs', label: 'Docs', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n Expanded\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n Collapsed\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```\n\n## Embed in Layout sider\n\nRecommended shell: **global Header + inner `has-sider` Layout**. Bind menu `collapsed` to `MLayoutSider`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n MMenu,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n {\n key: 'system',\n label: 'System',\n icon: 'settings',\n items: [\n { key: 'users', label: 'Users', icon: 'users' },\n { key: 'roles', label: 'Roles', icon: 'shield' },\n ],\n },\n]\n</script>\n\n<template>\n <MLayout\n style=\"\n height: 14rem;\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutHeader\n bordered\n style=\"\n padding: 0 var(--m-space-4);\n display: flex;\n align-items: center;\n min-height: var(--m-layout-header-height);\n \"\n >\n <strong style=\"color: var(--m-color-primary); font-size: var(--m-font-size-md)\">Morya UI</strong>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n collapse-mode=\"width\"\n :collapsed-width=\"64\"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"64\"\n accordion\n />\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding: var(--m-space-4)\">\n <p style=\"margin: 0; color: var(--m-color-text-muted); font-size: var(--m-font-size-sm)\">\n Selected: <strong style=\"color: var(--m-color-text)\">{{ selectedKey }}</strong>\n </p>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Horizontal mode\n\n`mode=\"horizontal\"` for top nav bars. Submenus open in a `MPopover` dropdown flyout (teleported, themed scrollbar) and close after selection. Popup mode root menus also use built-in `MScrollbar`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n { key: 'home', label: 'Home', icon: 'home' },\n {\n key: 'products',\n label: 'Products',\n icon: 'box',\n items: [\n { key: 'cloud', label: 'Cloud', icon: 'cloud' },\n { key: 'edge', label: 'Edge', icon: 'server' },\n ],\n },\n { key: 'about', label: 'About', icon: 'info-circle' },\n]\n</script>\n\n<template>\n <div\n style=\"\n padding: 0 var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" mode=\"horizontal\" embedded />\n </div>\n</template>\n```\n\n## Inverted (dark sider)\n\nUse `inverted` with `MLayoutSider`'s `inverted` on dark backgrounds.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutSider, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n { key: 'users', label: 'Users', icon: 'users' },\n]\n</script>\n\n<template>\n <MLayout\n has-sider\n style=\"\n height: 10rem;\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutSider inverted bordered style=\"width: 12rem\">\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" inverted />\n </MLayoutSider>\n </MLayout>\n</template>\n```\n\n## Popup mode\n\n`popup` + `v-model` renders a floating menu, teleported to `body` by default and positioned against the **default-slot trigger** (falls back to the last pointer position when no slot). Closes on outside click or leaf selection.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [\n { label: 'Copy', command: () => undefined },\n { separator: true },\n { label: 'Delete', disabled: true },\n]\n</script>\n\n<template>\n <MMenu v-model=\"open\" popup :model=\"model\">\n <MButton label=\"More\" @click=\"open = !open\" />\n </MMenu>\n</template>\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `model` | `MenuItem[]` | — | Menu items; may nest `items`. |\n| `popup` | `boolean` | `false` | Overlay mode; use with `v-model`. |\n| `modelValue` | `boolean` | `false` | Popup visibility (`v-model`). |\n| `placement` | `'bottom-start' \\| 'bottom-end' \\| 'top-start' \\| 'top-end'` | `'bottom-start'` | Popup position relative to the trigger. |\n| `selectedKey` | `string \\| null` | `null` | Selected item key (`v-model:selected-key`). |\n| `collapsed` | `boolean` | `false` | Icon-only; submenus in right flyout. |\n| `collapsedWidth` | `number` | `80` | Collapsed width (px) for icon centering. |\n| `indent` | `number` | `12` | Extra padding-left per level (px). |\n| `rootIndent` | `number` | `16` | Root item padding-left (px). |\n| `accordion` | `boolean` | `false` | Only one top-level submenu open at a time. |\n| `defaultExpandedKeys` | `string[]` | `[]` | Initially expanded submenu keys. |\n| `expandedKeys` | `string[]` | — | Controlled expanded keys (`v-model:expanded-keys`). |\n| `defaultExpandAll` | `boolean` | `false` | Expand all submenus initially. |\n| `mode` | `'vertical' \\| 'horizontal'` | `'vertical'` | Layout direction. |\n| `inverted` | `boolean` | `false` | Inverted colors for dark sider. |\n| `embedded` | `boolean` | `!popup` | Embed in layout (no border/min-width). |\n| `teleport` | `boolean` | `true` | Teleport popup to `appendTo`. |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Teleport target; falls back to ConfigProvider. |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | Pass-through; see [Styling & attrs](/docs/attrs). |\n\n\n## Events\n\n| Event | Payload | Description |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | Popup visibility changed. |\n| `update:selectedKey` | `string \\| null` | Selected item changed. |\n| `update:expandedKeys` | `string[]` | Expanded keys changed. |\n| `select` | `MenuItem` | Leaf clicked (not disabled / separator). |\n\n## Slots\n\n| Slot | Description |\n| --- | --- |\n| `default` | Popup trigger anchor (e.g. a button); menu positions against it. |\n\n## MenuItem\n\n| Field | Type | Description |\n| --- | --- | --- |\n| `key` | `string` | Unique id; falls back to `label`. |\n| `label` | `string` | Display text. |\n| `icon` | `string` | [Tabler icon name](/components/Icon) or character. |\n| `command` | `() => void` | Click handler (also emits `select`). |\n| `disabled` | `boolean` | Disabled state. |\n| `separator` | `boolean` | Separator line (ignores other fields). |\n| `items` | `MenuItem[]` | Child menu items. |\n\n## Types\n\n<h4 id=\"MenuItem\">MenuItem</h4>\n\nEach entry in `model`; supports nesting:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // RouterLink when vue-router is installed\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\nWhen `key` is omitted, `label` is used as the identifier. `separator: true` renders a divider. See also [API types](/docs/types).\n"
|
|
12790
12810
|
}
|
|
12791
12811
|
}
|
|
12792
12812
|
},
|
|
@@ -15648,7 +15668,7 @@
|
|
|
15648
15668
|
"sectionId": "基础用法",
|
|
15649
15669
|
"lang": "vue",
|
|
15650
15670
|
"preview": true,
|
|
15651
|
-
"code": "<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width:
|
|
15671
|
+
"code": "<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width: 100%\">\n <p\n v-for=\"item in 20\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-primary) 12%, transparent);\n color:var(--m-color-primary);\n \"\n >\n {{ item }}\n </p>\n </MScrollbar>\n</template>",
|
|
15652
15672
|
"locale": "zh-CN"
|
|
15653
15673
|
},
|
|
15654
15674
|
{
|
|
@@ -15657,7 +15677,7 @@
|
|
|
15657
15677
|
"sectionId": "max-height",
|
|
15658
15678
|
"lang": "vue",
|
|
15659
15679
|
"preview": true,
|
|
15660
|
-
"code": "<script setup lang=\"ts\">\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst count = ref(3)\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;gap:0.5rem\">\n <MButton label=\"Add\" size=\"small\" @click=\"count++\" />\n <MButton label=\"Remove\" severity=\"secondary\" size=\"small\" @click=\"count = Math.max(0, count - 1)\" />\n </div>\n <MScrollbar max-height=\"220px\" always style=\"width:
|
|
15680
|
+
"code": "<script setup lang=\"ts\">\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst count = ref(3)\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;gap:0.5rem\">\n <MButton label=\"Add\" size=\"small\" @click=\"count++\" />\n <MButton label=\"Remove\" severity=\"secondary\" size=\"small\" @click=\"count = Math.max(0, count - 1)\" />\n </div>\n <MScrollbar max-height=\"220px\" always style=\"width: 100%\">\n <p\n v-for=\"item in count\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-info) 12%, transparent);\n color:var(--m-color-info);\n \"\n >\n Item {{ item }}\n </p>\n </MScrollbar>\n </div>\n</template>",
|
|
15661
15681
|
"locale": "zh-CN"
|
|
15662
15682
|
},
|
|
15663
15683
|
{
|
|
@@ -15729,7 +15749,7 @@
|
|
|
15729
15749
|
"sectionId": "basic",
|
|
15730
15750
|
"lang": "vue",
|
|
15731
15751
|
"preview": true,
|
|
15732
|
-
"code": "<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width:
|
|
15752
|
+
"code": "<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width: 100%\">\n <p\n v-for=\"item in 20\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-primary) 12%, transparent);\n color:var(--m-color-primary);\n \"\n >\n {{ item }}\n </p>\n </MScrollbar>\n</template>",
|
|
15733
15753
|
"locale": "en-US"
|
|
15734
15754
|
},
|
|
15735
15755
|
{
|
|
@@ -15738,7 +15758,7 @@
|
|
|
15738
15758
|
"sectionId": "max-height",
|
|
15739
15759
|
"lang": "vue",
|
|
15740
15760
|
"preview": true,
|
|
15741
|
-
"code": "<script setup lang=\"ts\">\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst count = ref(3)\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;gap:0.5rem\">\n <MButton label=\"Add\" size=\"small\" @click=\"count++\" />\n <MButton label=\"Remove\" severity=\"secondary\" size=\"small\" @click=\"count = Math.max(0, count - 1)\" />\n </div>\n <MScrollbar max-height=\"220px\" always style=\"width:
|
|
15761
|
+
"code": "<script setup lang=\"ts\">\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst count = ref(3)\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;gap:0.5rem\">\n <MButton label=\"Add\" size=\"small\" @click=\"count++\" />\n <MButton label=\"Remove\" severity=\"secondary\" size=\"small\" @click=\"count = Math.max(0, count - 1)\" />\n </div>\n <MScrollbar max-height=\"220px\" always style=\"width: 100%\">\n <p\n v-for=\"item in count\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-info) 12%, transparent);\n color:var(--m-color-info);\n \"\n >\n Item {{ item }}\n </p>\n </MScrollbar>\n </div>\n</template>",
|
|
15742
15762
|
"locale": "en-US"
|
|
15743
15763
|
},
|
|
15744
15764
|
{
|
|
@@ -15796,17 +15816,17 @@
|
|
|
15796
15816
|
{
|
|
15797
15817
|
"id": "基础用法",
|
|
15798
15818
|
"title": "基础用法",
|
|
15799
|
-
"body": "用 `height` 固定可视区域高度;不设时跟随父容器高度。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width:
|
|
15819
|
+
"body": "用 `height` 固定可视区域高度;不设时跟随父容器高度。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width: 100%\">\n <p\n v-for=\"item in 20\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-primary) 12%, transparent);\n color:var(--m-color-primary);\n \"\n >\n {{ item }}\n </p>\n </MScrollbar>\n</template>\n```"
|
|
15800
15820
|
},
|
|
15801
15821
|
{
|
|
15802
15822
|
"id": "max-height",
|
|
15803
15823
|
"title": "Max height",
|
|
15804
|
-
"body": "仅当内容超出 `max-height` 时出现滚动条。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst count = ref(3)\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;gap:0.5rem\">\n <MButton label=\"Add\" size=\"small\" @click=\"count++\" />\n <MButton label=\"Remove\" severity=\"secondary\" size=\"small\" @click=\"count = Math.max(0, count - 1)\" />\n </div>\n <MScrollbar max-height=\"220px\" always style=\"width:
|
|
15824
|
+
"body": "仅当内容超出 `max-height` 时出现滚动条。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst count = ref(3)\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;gap:0.5rem\">\n <MButton label=\"Add\" size=\"small\" @click=\"count++\" />\n <MButton label=\"Remove\" severity=\"secondary\" size=\"small\" @click=\"count = Math.max(0, count - 1)\" />\n </div>\n <MScrollbar max-height=\"220px\" always style=\"width: 100%\">\n <p\n v-for=\"item in count\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-info) 12%, transparent);\n color:var(--m-color-info);\n \"\n >\n Item {{ item }}\n </p>\n </MScrollbar>\n </div>\n</template>\n```"
|
|
15805
15825
|
},
|
|
15806
15826
|
{
|
|
15807
15827
|
"id": "horizontal",
|
|
15808
15828
|
"title": "Horizontal",
|
|
15809
|
-
"body": "内容宽度超出容器时显示横向滚动条。`trigger=\"none\"` 与 `always` 都会常显滑块;默认 `trigger=\"hover\"` 在悬停时显示。\n\n`MLayout`
|
|
15829
|
+
"body": "内容宽度超出容器时显示横向滚动条。`trigger=\"none\"` 与 `always` 都会常显滑块;默认 `trigger=\"hover\"` 在悬停时显示。\n\n`MLayout` / `MLayoutContent` / `MLayoutSider`、`MDialog`、`MConfirmDialog`、`MDrawer`、`MSplitter`、`MTable`、`MSelect`、`MTreeSelect`、`MDropdown`、`MContextMenu`、`MPopover`、`MConfirmPopup`、`MMenu`(popup)、`MMenubar`、`MTieredMenu`、`MTabs`、`MGallery`、`MTimeline`(horizontal)、`MTextarea`(autosize `maxRows`)、`MTerminal`、`MOrderList`、`MPickList`、`MTreeTable`、`MVirtualScroller` 等组件已内置本组件。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar always>\n <div style=\"display:flex;width:fit-content\">\n <p\n v-for=\"item in 30\"\n :key=\"item\"\n style=\"\n flex-shrink:0;display:flex;align-items:center;justify-content:center;\n width:96px;height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-danger) 12%, transparent);\n color:var(--m-color-danger);\n \"\n >\n {{ item }}\n </p>\n </div>\n </MScrollbar>\n</template>\n```"
|
|
15810
15830
|
},
|
|
15811
15831
|
{
|
|
15812
15832
|
"id": "always-native",
|
|
@@ -15844,7 +15864,7 @@
|
|
|
15844
15864
|
"body": "<h4 id=\"ScrollbarAriaOrientation\">ScrollbarAriaOrientation</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ntype ScrollbarAriaOrientation = 'horizontal' | 'vertical'\n```\n\n<h4 id=\"ScrollbarClassValue\">ScrollbarClassValue</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ntype ScrollbarClassValue = | string\n | string[]\n | Record<string, boolean>\n | Array<string | Record<string, boolean> | null | undefined | false>\n```"
|
|
15845
15865
|
}
|
|
15846
15866
|
],
|
|
15847
|
-
"markdown": "---\ntitle: Scrollbar\ncategory: 01 / BASIC\ndescription: 可换肤自定义滚动条,提供一致的滚动体验。\n---\n\n# Scrollbar\n\n用于替换浏览器原生滚动条,提供跨浏览器一致的可换肤滚动体验。\n\n## 引入\n\n```ts\nimport { MScrollbar } from 'morya-ui'\n```\n\n## 基础用法\n\n用 `height` 固定可视区域高度;不设时跟随父容器高度。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width:
|
|
15867
|
+
"markdown": "---\ntitle: Scrollbar\ncategory: 01 / BASIC\ndescription: 可换肤自定义滚动条,提供一致的滚动体验。\n---\n\n# Scrollbar\n\n用于替换浏览器原生滚动条,提供跨浏览器一致的可换肤滚动体验。\n\n## 引入\n\n```ts\nimport { MScrollbar } from 'morya-ui'\n```\n\n## 基础用法\n\n用 `height` 固定可视区域高度;不设时跟随父容器高度。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width: 100%\">\n <p\n v-for=\"item in 20\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-primary) 12%, transparent);\n color:var(--m-color-primary);\n \"\n >\n {{ item }}\n </p>\n </MScrollbar>\n</template>\n```\n\n## Max height\n\n仅当内容超出 `max-height` 时出现滚动条。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst count = ref(3)\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;gap:0.5rem\">\n <MButton label=\"Add\" size=\"small\" @click=\"count++\" />\n <MButton label=\"Remove\" severity=\"secondary\" size=\"small\" @click=\"count = Math.max(0, count - 1)\" />\n </div>\n <MScrollbar max-height=\"220px\" always style=\"width: 100%\">\n <p\n v-for=\"item in count\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-info) 12%, transparent);\n color:var(--m-color-info);\n \"\n >\n Item {{ item }}\n </p>\n </MScrollbar>\n </div>\n</template>\n```\n\n## Horizontal\n\n内容宽度超出容器时显示横向滚动条。`trigger=\"none\"` 与 `always` 都会常显滑块;默认 `trigger=\"hover\"` 在悬停时显示。\n\n`MLayout` / `MLayoutContent` / `MLayoutSider`、`MDialog`、`MConfirmDialog`、`MDrawer`、`MSplitter`、`MTable`、`MSelect`、`MTreeSelect`、`MDropdown`、`MContextMenu`、`MPopover`、`MConfirmPopup`、`MMenu`(popup)、`MMenubar`、`MTieredMenu`、`MTabs`、`MGallery`、`MTimeline`(horizontal)、`MTextarea`(autosize `maxRows`)、`MTerminal`、`MOrderList`、`MPickList`、`MTreeTable`、`MVirtualScroller` 等组件已内置本组件。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar always>\n <div style=\"display:flex;width:fit-content\">\n <p\n v-for=\"item in 30\"\n :key=\"item\"\n style=\"\n flex-shrink:0;display:flex;align-items:center;justify-content:center;\n width:96px;height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-danger) 12%, transparent);\n color:var(--m-color-danger);\n \"\n >\n {{ item }}\n </p>\n </div>\n </MScrollbar>\n</template>\n```\n\n## Always / Native\n\n`always` 常显自定义滑块;`native` 使用浏览器原生滚动条。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"display:grid;gap:1.25rem;grid-template-columns:1fr 1fr\">\n <div>\n <p style=\"margin:0 0 0.5rem;color:var(--m-color-text-muted)\">\n always\n </p>\n <MScrollbar height=\"160px\" always>\n <p v-for=\"n in 12\" :key=\"n\" style=\"margin:0.5rem 0\">\n Line {{ n }}\n </p>\n </MScrollbar>\n </div>\n <div>\n <p style=\"margin:0 0 0.5rem;color:var(--m-color-text-muted)\">\n native\n </p>\n <MScrollbar height=\"160px\" native style=\"width: 200px\">\n <p v-for=\"n in 12\" :key=\"n\" style=\"margin:0.5rem 0\">\n Line {{ n }}\n </p>\n </MScrollbar>\n </div>\n </div>\n</template>\n```\n\n## Manual scroll\n\n通过实例方法 `setScrollTop` / `setScrollLeft` / `scrollTo` / `update` 控制滚动。\n\n```vue preview\n<script setup lang=\"ts\">\nimport type { ScrollbarInstance } from 'morya-ui'\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst scrollbarRef = ref<ScrollbarInstance>()\nconst scrollTop = ref(0)\n\nfunction jump(top: number) {\n scrollbarRef.value?.setScrollTop(top)\n}\n\nfunction onScroll(payload: { scrollTop: number }) {\n scrollTop.value = Math.round(payload.scrollTop)\n}\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;flex-wrap:wrap;gap:0.5rem;align-items:center\">\n <MButton label=\"Top\" size=\"small\" @click=\"jump(0)\" />\n <MButton label=\"Mid\" size=\"small\" severity=\"secondary\" @click=\"jump(200)\" />\n <MButton label=\"Bottom\" size=\"small\" severity=\"secondary\" @click=\"jump(9999)\" />\n <span style=\"color:var(--m-color-text-muted);font-size:0.875rem\">scrollTop: {{ scrollTop }}</span>\n </div>\n <MScrollbar ref=\"scrollbarRef\" height=\"200px\" style=\"width: 200px\" always @scroll=\"onScroll\">\n <p\n v-for=\"item in 24\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-success) 12%, transparent);\n color:var(--m-color-success);\n \"\n >\n {{ item }}\n </p>\n </MScrollbar>\n </div>\n</template>\n```\n\n## Infinite scroll\n\n滚动到边缘时触发 `end-reached`,可用于无限加载。\n\n```vue preview\n<script setup lang=\"ts\">\nimport type { ScrollbarDirection } from 'morya-ui'\nimport { MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst num = ref(20)\n\nfunction loadMore(direction: ScrollbarDirection) {\n if (direction === 'bottom') num.value += 5\n}\n</script>\n\n<template>\n <MScrollbar height=\"220px\" always style=\"width: 200px\" @end-reached=\"loadMore\">\n <p\n v-for=\"item in num\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-primary) 12%, transparent);\n color:var(--m-color-primary);\n \"\n >\n {{ item }}\n </p>\n </MScrollbar>\n</template>\n```\n\n## API\n\n### Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `always` | `boolean` | — | — |\n| `ariaLabel` | `string` | — | — |\n| `ariaOrientation` | `ScrollbarAriaOrientation` | — | — |\n| `distance` | `number` | — | — |\n| `height` | `string \\| number` | — | — |\n| `id` | `string` | — | — |\n| `maxHeight` | `string \\| number` | — | — |\n| `minSize` | `number` | — | — |\n| `native` | `boolean` | — | — |\n| `noresize` | `boolean` | — | — |\n| `role` | `string` | — | — |\n| `tabindex` | `number \\| string` | — | — |\n| `tag` | `string` | — | — |\n| `trigger` | `'hover' \\| 'none'` | — | — |\n| `viewClass` | `ScrollbarClassValue` | — | — |\n| `viewStyle` | `StyleValue` | — | — |\n| `wrapClass` | `ScrollbarClassValue` | — | — |\n| `wrapStyle` | `StyleValue` | — | — |\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| height | `string \\| number` | — | 可视区域高度 |\n| maxHeight | `string \\| number` | — | 最大高度 |\n| fitContent | `boolean` | `false` | 随内容增高,配合根节点 CSS `max-height` 使用(下拉面板) |\n| native | `boolean` | `false` | 使用原生滚动条 |\n| wrapStyle / wrapClass | style / class | — | wrap 容器样式 |\n| viewStyle / viewClass | style / class | — | 内容区样式 |\n| noresize | `boolean` | `false` | 不监听尺寸变化 |\n| tag | `string` | `div` | 内容区标签 |\n| always | `boolean` | `false` | 始终显示滑块 |\n| trigger | `'hover' \\| 'none'` | `'hover'` | `none` 常显滑块;`always` 为 true 时仍常显 |\n| minSize | `number` | `20` | 滑块最小尺寸 |\n| id / role / ariaLabel / ariaOrientation | a11y | — | 内容区无障碍属性 |\n| tabindex | `number \\| string` | — | wrap 的 tabindex |\n| distance | `number` | `0` | 触发 `end-reached` 的边缘距离 |\n\n### Events\n\n| Name | Payload |\n| --- | --- |\n| scroll | `{ scrollTop, scrollLeft }` |\n| end-reached | `'top' \\| 'bottom' \\| 'left' \\| 'right'` |\n\n### Expose\n\n`wrapRef`、`update`、`scrollTo`、`setScrollTop`、`setScrollLeft`、`handleScroll`\n\n## Events\n\n| 事件名 | 参数 | 说明 |\n| --- | --- | --- |\n| `scroll` | `{ scrollTop, scrollLeft }` | 滚动位置变化。 |\n| `end-reached` | `'top' \\| 'bottom' \\| 'left' \\| 'right'` | 滚动到边缘。 |\n\n## Slots\n\n| 插槽名 | 说明 |\n| --- | --- |\n| `default` | 可滚动内容。 |\n\n## 类型\n\n<h4 id=\"ScrollbarAriaOrientation\">ScrollbarAriaOrientation</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ntype ScrollbarAriaOrientation = 'horizontal' | 'vertical'\n```\n\n<h4 id=\"ScrollbarClassValue\">ScrollbarClassValue</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ntype ScrollbarClassValue = | string\n | string[]\n | Record<string, boolean>\n | Array<string | Record<string, boolean> | null | undefined | false>\n```\n"
|
|
15848
15868
|
},
|
|
15849
15869
|
"en-US": {
|
|
15850
15870
|
"title": "Scrollbar",
|
|
@@ -15863,17 +15883,17 @@
|
|
|
15863
15883
|
{
|
|
15864
15884
|
"id": "basic",
|
|
15865
15885
|
"title": "Basic",
|
|
15866
|
-
"body": "Use `height` to fix the viewport height. If omitted, it follows the parent height.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width:
|
|
15886
|
+
"body": "Use `height` to fix the viewport height. If omitted, it follows the parent height.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width: 100%\">\n <p\n v-for=\"item in 20\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-primary) 12%, transparent);\n color:var(--m-color-primary);\n \"\n >\n {{ item }}\n </p>\n </MScrollbar>\n</template>\n```"
|
|
15867
15887
|
},
|
|
15868
15888
|
{
|
|
15869
15889
|
"id": "max-height",
|
|
15870
15890
|
"title": "Max height",
|
|
15871
|
-
"body": "The scrollbar appears only when content exceeds `max-height`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst count = ref(3)\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;gap:0.5rem\">\n <MButton label=\"Add\" size=\"small\" @click=\"count++\" />\n <MButton label=\"Remove\" severity=\"secondary\" size=\"small\" @click=\"count = Math.max(0, count - 1)\" />\n </div>\n <MScrollbar max-height=\"220px\" always style=\"width:
|
|
15891
|
+
"body": "The scrollbar appears only when content exceeds `max-height`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst count = ref(3)\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;gap:0.5rem\">\n <MButton label=\"Add\" size=\"small\" @click=\"count++\" />\n <MButton label=\"Remove\" severity=\"secondary\" size=\"small\" @click=\"count = Math.max(0, count - 1)\" />\n </div>\n <MScrollbar max-height=\"220px\" always style=\"width: 100%\">\n <p\n v-for=\"item in count\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-info) 12%, transparent);\n color:var(--m-color-info);\n \"\n >\n Item {{ item }}\n </p>\n </MScrollbar>\n </div>\n</template>\n```"
|
|
15872
15892
|
},
|
|
15873
15893
|
{
|
|
15874
15894
|
"id": "horizontal",
|
|
15875
15895
|
"title": "Horizontal",
|
|
15876
|
-
"body": "A horizontal scrollbar appears when content is wider than the container. `trigger=\"none\"` and `always` keep the thumb visible; the default `trigger=\"hover\"` shows it on hover.\n\n`MLayout`
|
|
15896
|
+
"body": "A horizontal scrollbar appears when content is wider than the container. `trigger=\"none\"` and `always` keep the thumb visible; the default `trigger=\"hover\"` shows it on hover.\n\n`MLayout`, `MLayoutContent`, `MLayoutSider`, `MDialog`, `MConfirmDialog`, `MDrawer`, `MSplitter`, `MTable`, `MSelect`, `MTreeSelect`, `MDropdown`, `MContextMenu`, `MPopover`, `MConfirmPopup`, `MMenu` (popup), `MMenubar`, `MTieredMenu`, `MTabs`, `MGallery`, `MTimeline` (horizontal), `MTextarea` (autosize `maxRows`), `MTerminal`, `MOrderList`, `MPickList`, `MTreeTable`, and `MVirtualScroller` integrate this component internally.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar always>\n <div style=\"display:flex;width:fit-content\">\n <p\n v-for=\"item in 30\"\n :key=\"item\"\n style=\"\n flex-shrink:0;display:flex;align-items:center;justify-content:center;\n width:96px;height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-danger) 12%, transparent);\n color:var(--m-color-danger);\n \"\n >\n {{ item }}\n </p>\n </div>\n </MScrollbar>\n</template>\n```"
|
|
15877
15897
|
},
|
|
15878
15898
|
{
|
|
15879
15899
|
"id": "always-native",
|
|
@@ -15906,7 +15926,7 @@
|
|
|
15906
15926
|
"body": "| Slot | Description |\n| --- | --- |\n| `default` | Scrollable content. |"
|
|
15907
15927
|
}
|
|
15908
15928
|
],
|
|
15909
|
-
"markdown": "---\ntitle: Scrollbar\ncategory: 01 / BASIC\ndescription: Themeable custom scrollbar for a consistent scrolling experience.\n---\n\n# Scrollbar\n\nReplaces the native browser scrollbar with a themeable, cross-browser scrolling experience.\n\n## Import\n\n```ts\nimport { MScrollbar } from 'morya-ui'\n```\n\n## Basic\n\nUse `height` to fix the viewport height. If omitted, it follows the parent height.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width:
|
|
15929
|
+
"markdown": "---\ntitle: Scrollbar\ncategory: 01 / BASIC\ndescription: Themeable custom scrollbar for a consistent scrolling experience.\n---\n\n# Scrollbar\n\nReplaces the native browser scrollbar with a themeable, cross-browser scrolling experience.\n\n## Import\n\n```ts\nimport { MScrollbar } from 'morya-ui'\n```\n\n## Basic\n\nUse `height` to fix the viewport height. If omitted, it follows the parent height.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar height=\"240px\" always style=\"width: 100%\">\n <p\n v-for=\"item in 20\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-primary) 12%, transparent);\n color:var(--m-color-primary);\n \"\n >\n {{ item }}\n </p>\n </MScrollbar>\n</template>\n```\n\n## Max height\n\nThe scrollbar appears only when content exceeds `max-height`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst count = ref(3)\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;gap:0.5rem\">\n <MButton label=\"Add\" size=\"small\" @click=\"count++\" />\n <MButton label=\"Remove\" severity=\"secondary\" size=\"small\" @click=\"count = Math.max(0, count - 1)\" />\n </div>\n <MScrollbar max-height=\"220px\" always style=\"width: 100%\">\n <p\n v-for=\"item in count\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-info) 12%, transparent);\n color:var(--m-color-info);\n \"\n >\n Item {{ item }}\n </p>\n </MScrollbar>\n </div>\n</template>\n```\n\n## Horizontal\n\nA horizontal scrollbar appears when content is wider than the container. `trigger=\"none\"` and `always` keep the thumb visible; the default `trigger=\"hover\"` shows it on hover.\n\n`MLayout`, `MLayoutContent`, `MLayoutSider`, `MDialog`, `MConfirmDialog`, `MDrawer`, `MSplitter`, `MTable`, `MSelect`, `MTreeSelect`, `MDropdown`, `MContextMenu`, `MPopover`, `MConfirmPopup`, `MMenu` (popup), `MMenubar`, `MTieredMenu`, `MTabs`, `MGallery`, `MTimeline` (horizontal), `MTextarea` (autosize `maxRows`), `MTerminal`, `MOrderList`, `MPickList`, `MTreeTable`, and `MVirtualScroller` integrate this component internally.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <MScrollbar always>\n <div style=\"display:flex;width:fit-content\">\n <p\n v-for=\"item in 30\"\n :key=\"item\"\n style=\"\n flex-shrink:0;display:flex;align-items:center;justify-content:center;\n width:96px;height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-danger) 12%, transparent);\n color:var(--m-color-danger);\n \"\n >\n {{ item }}\n </p>\n </div>\n </MScrollbar>\n</template>\n```\n\n## Always / Native\n\n`always` keeps the custom thumb visible. `native` uses the browser scrollbar.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"display:grid;gap:1.25rem;grid-template-columns:1fr 1fr\">\n <div>\n <p style=\"margin:0 0 0.5rem;color:var(--m-color-text-muted)\">\n always\n </p>\n <MScrollbar height=\"160px\" always>\n <p v-for=\"n in 12\" :key=\"n\" style=\"margin:0.5rem 0\">\n Line {{ n }}\n </p>\n </MScrollbar>\n </div>\n <div>\n <p style=\"margin:0 0 0.5rem;color:var(--m-color-text-muted)\">\n native\n </p>\n <MScrollbar height=\"160px\" native style=\"width: 200px\">\n <p v-for=\"n in 12\" :key=\"n\" style=\"margin:0.5rem 0\">\n Line {{ n }}\n </p>\n </MScrollbar>\n </div>\n </div>\n</template>\n```\n\n## Manual scroll\n\nControl scrolling with instance methods `setScrollTop` / `setScrollLeft` / `scrollTo` / `update`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport type { ScrollbarInstance } from 'morya-ui'\nimport { MButton, MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst scrollbarRef = ref<ScrollbarInstance>()\nconst scrollTop = ref(0)\n\nfunction jump(top: number) {\n scrollbarRef.value?.setScrollTop(top)\n}\n\nfunction onScroll(payload: { scrollTop: number }) {\n scrollTop.value = Math.round(payload.scrollTop)\n}\n</script>\n\n<template>\n <div style=\"display:grid;gap:0.75rem\">\n <div style=\"display:flex;flex-wrap:wrap;gap:0.5rem;align-items:center\">\n <MButton label=\"Top\" size=\"small\" @click=\"jump(0)\" />\n <MButton label=\"Mid\" size=\"small\" severity=\"secondary\" @click=\"jump(200)\" />\n <MButton label=\"Bottom\" size=\"small\" severity=\"secondary\" @click=\"jump(9999)\" />\n <span style=\"color:var(--m-color-text-muted);font-size:0.875rem\">scrollTop: {{ scrollTop }}</span>\n </div>\n <MScrollbar ref=\"scrollbarRef\" height=\"200px\" always style=\"width: 200px\" @scroll=\"onScroll\">\n <p\n v-for=\"item in 24\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-success) 12%, transparent);\n color:var(--m-color-success);\n \"\n >\n {{ item }}\n </p>\n </MScrollbar>\n </div>\n</template>\n```\n\n## Infinite scroll\n\nEmits `end-reached` at the edge. Use it for infinite loading.\n\n```vue preview\n<script setup lang=\"ts\">\nimport type { ScrollbarDirection } from 'morya-ui'\nimport { MScrollbar } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst num = ref(20)\n\nfunction loadMore(direction: ScrollbarDirection) {\n if (direction === 'bottom') num.value += 5\n}\n</script>\n\n<template>\n <MScrollbar height=\"220px\" style=\"width: 200px\" always @end-reached=\"loadMore\">\n <p\n v-for=\"item in num\"\n :key=\"item\"\n style=\"\n display:flex;align-items:center;justify-content:center;\n height:48px;margin:8px;border-radius:6px;\n background:color-mix(in srgb, var(--m-color-primary) 12%, transparent);\n color:var(--m-color-primary);\n \"\n >\n {{ item }}\n </p>\n </MScrollbar>\n</template>\n```\n\n## API\n\n### Props\n\n| Name | Type | Default | Description |\n| --- | --- | --- | --- |\n| height | `string \\| number` | — | Viewport height |\n| maxHeight | `string \\| number` | — | Maximum height |\n| fitContent | `boolean` | `false` | Grow with content; pair with CSS `max-height` on the root (dropdown panels) |\n| native | `boolean` | `false` | Use the native scrollbar |\n| wrapStyle / wrapClass | style / class | — | Wrap container styles |\n| viewStyle / viewClass | style / class | — | Content area styles |\n| noresize | `boolean` | `false` | Do not listen for size changes |\n| tag | `string` | `div` | Content area tag |\n| always | `boolean` | `false` | Always show the thumb |\n| trigger | `'hover' \\| 'none'` | `'hover'` | `none` keeps thumbs visible; `always` still wins |\n| minSize | `number` | `20` | Minimum thumb size |\n| id / role / ariaLabel / ariaOrientation | a11y | — | Accessible attributes for the content area |\n| tabindex | `number \\| string` | — | tabindex on the wrap |\n| distance | `number` | `0` | Edge distance that triggers `end-reached` |\n\n### Events\n\n| Name | Payload |\n| --- | --- |\n| scroll | `{ scrollTop, scrollLeft }` |\n| end-reached | `'top' \\| 'bottom' \\| 'left' \\| 'right'` |\n\n### Expose\n\n`wrapRef`, `update`, `scrollTo`, `setScrollTop`, `setScrollLeft`, `handleScroll`\n\n## Events\n\n| Event | Payload | Description |\n| --- | --- | --- |\n| `scroll` | `{ scrollTop, scrollLeft }` | Scroll position change. |\n| `end-reached` | `'top' \\| 'bottom' \\| 'left' \\| 'right'` | Scroll boundary reached. |\n\n## Slots\n\n| Slot | Description |\n| --- | --- |\n| `default` | Scrollable content. |\n"
|
|
15910
15930
|
}
|
|
15911
15931
|
}
|
|
15912
15932
|
},
|
|
@@ -15985,7 +16005,7 @@
|
|
|
15985
16005
|
"sectionId": "基础用法",
|
|
15986
16006
|
"lang": "vue",
|
|
15987
16007
|
"preview": true,
|
|
15988
|
-
"code": "<script setup lang=\"ts\">\nimport { MScrollTop } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem;
|
|
16008
|
+
"code": "<script setup lang=\"ts\">\nimport { MScrollTop, MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem; position: relative\">\n <MScrollbar height=\"8rem\">\n <div style=\"height: 40rem; padding: var(--m-space-3)\">\n 向下滚动…\n </div>\n <MScrollTop :threshold=\"80\" target=\"parent\" :right=\"16\" :bottom=\"16\" />\n </MScrollbar>\n </div>\n</template>",
|
|
15989
16009
|
"locale": "zh-CN"
|
|
15990
16010
|
},
|
|
15991
16011
|
{
|
|
@@ -16003,7 +16023,7 @@
|
|
|
16003
16023
|
"sectionId": "basic",
|
|
16004
16024
|
"lang": "vue",
|
|
16005
16025
|
"preview": true,
|
|
16006
|
-
"code": "<script setup lang=\"ts\">\nimport { MScrollTop } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem;
|
|
16026
|
+
"code": "<script setup lang=\"ts\">\nimport { MScrollTop, MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem; position: relative\">\n <MScrollbar height=\"8rem\">\n <div style=\"height: 40rem; padding: var(--m-space-3)\">\n Scroll down…\n </div>\n <MScrollTop :threshold=\"80\" target=\"parent\" :right=\"16\" :bottom=\"16\" />\n </MScrollbar>\n </div>\n</template>",
|
|
16007
16027
|
"locale": "en-US"
|
|
16008
16028
|
}
|
|
16009
16029
|
],
|
|
@@ -16025,7 +16045,12 @@
|
|
|
16025
16045
|
{
|
|
16026
16046
|
"id": "基础用法",
|
|
16027
16047
|
"title": "基础用法",
|
|
16028
|
-
"body": "```vue preview\n<script setup lang=\"ts\">\nimport { MScrollTop } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem;
|
|
16048
|
+
"body": "```vue preview\n<script setup lang=\"ts\">\nimport { MScrollTop, MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem; position: relative\">\n <MScrollbar height=\"8rem\">\n <div style=\"height: 40rem; padding: var(--m-space-3)\">\n 向下滚动…\n </div>\n <MScrollTop :threshold=\"80\" target=\"parent\" :right=\"16\" :bottom=\"16\" />\n </MScrollbar>\n </div>\n</template>\n```"
|
|
16049
|
+
},
|
|
16050
|
+
{
|
|
16051
|
+
"id": "与-mscrollbar-配合",
|
|
16052
|
+
"title": "与 MScrollbar 配合",
|
|
16053
|
+
"body": "`target=\"parent\"` 时会向上查找最近的 `.m-scrollbar__wrap`(或原生 overflow 容器)作为滚动源。推荐将 `MScrollTop` 放在 `MScrollbar` 内容 slot 内,并保留 `teleport`(默认)以便按钮固定在视口角落。"
|
|
16029
16054
|
},
|
|
16030
16055
|
{
|
|
16031
16056
|
"id": "props",
|
|
@@ -16043,7 +16068,7 @@
|
|
|
16043
16068
|
"body": "| 插槽名 | 说明 |\n| --- | --- |\n| `default` | 默认内容。 |"
|
|
16044
16069
|
}
|
|
16045
16070
|
],
|
|
16046
|
-
"markdown": "---\ntitle: ScrollTop\ncategory: 04 / NAVIGATION\ndescription: 滚动超过阈值后显示回到顶部按钮。\n---\n\n# ScrollTop\n\n监听窗口或父容器滚动,一键回到顶部。\n\n## 引入\n\n```ts\nimport { MScrollTop } from 'morya-ui'\n```\n\n## 基础用法\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollTop } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem;
|
|
16071
|
+
"markdown": "---\ntitle: ScrollTop\ncategory: 04 / NAVIGATION\ndescription: 滚动超过阈值后显示回到顶部按钮。\n---\n\n# ScrollTop\n\n监听窗口或父容器滚动,一键回到顶部。\n\n## 引入\n\n```ts\nimport { MScrollTop } from 'morya-ui'\n```\n\n## 基础用法\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollTop, MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem; position: relative\">\n <MScrollbar height=\"8rem\">\n <div style=\"height: 40rem; padding: var(--m-space-3)\">\n 向下滚动…\n </div>\n <MScrollTop :threshold=\"80\" target=\"parent\" :right=\"16\" :bottom=\"16\" />\n </MScrollbar>\n </div>\n</template>\n```\n\n## 与 MScrollbar 配合\n\n`target=\"parent\"` 时会向上查找最近的 `.m-scrollbar__wrap`(或原生 overflow 容器)作为滚动源。推荐将 `MScrollTop` 放在 `MScrollbar` 内容 slot 内,并保留 `teleport`(默认)以便按钮固定在视口角落。\n\n## Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `threshold` | `number` | `400` | 显示阈值(px)。 |\n| `target` | `'window' \\| 'parent'` | `'window'` | 滚动目标。 |\n| `right` | `string \\| number` | — | 距右边缘;数字为 px。 |\n| `bottom` | `string \\| number` | — | 距底边缘;数字为 px。 |\n| `teleport` | `boolean` | `true` | 浮层 Teleport;默认挂到 `body`。 |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | 挂载目标;`'self'` / `false` 就地渲染。 |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | DOM 透传,见 [样式与 attrs](/docs/attrs). |\n\n\n## Events\n\n无自定义事件。\n\n## Slots\n\n| 插槽名 | 说明 |\n| --- | --- |\n| `default` | 默认内容。 |\n\n"
|
|
16047
16072
|
},
|
|
16048
16073
|
"en-US": {
|
|
16049
16074
|
"title": "ScrollTop",
|
|
@@ -16062,7 +16087,12 @@
|
|
|
16062
16087
|
{
|
|
16063
16088
|
"id": "basic",
|
|
16064
16089
|
"title": "Basic",
|
|
16065
|
-
"body": "```vue preview\n<script setup lang=\"ts\">\nimport { MScrollTop } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem;
|
|
16090
|
+
"body": "```vue preview\n<script setup lang=\"ts\">\nimport { MScrollTop, MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem; position: relative\">\n <MScrollbar height=\"8rem\">\n <div style=\"height: 40rem; padding: var(--m-space-3)\">\n Scroll down…\n </div>\n <MScrollTop :threshold=\"80\" target=\"parent\" :right=\"16\" :bottom=\"16\" />\n </MScrollbar>\n </div>\n</template>\n```"
|
|
16091
|
+
},
|
|
16092
|
+
{
|
|
16093
|
+
"id": "with-mscrollbar",
|
|
16094
|
+
"title": "With MScrollbar",
|
|
16095
|
+
"body": "When `target=\"parent\"`, ScrollTop walks up the DOM for the nearest `.m-scrollbar__wrap` (or native overflow container). Place `MScrollTop` inside the `MScrollbar` default slot and keep `teleport` enabled (default) so the button stays pinned to the viewport corner."
|
|
16066
16096
|
},
|
|
16067
16097
|
{
|
|
16068
16098
|
"id": "props",
|
|
@@ -16080,7 +16110,7 @@
|
|
|
16080
16110
|
"body": "No slots."
|
|
16081
16111
|
}
|
|
16082
16112
|
],
|
|
16083
|
-
"markdown": "---\ntitle: ScrollTop\ncategory: 04 / NAVIGATION\ndescription: Shows a back-to-top button after scrolling past a threshold.\n---\n\n# ScrollTop\n\nListens to window or parent scroll and jumps back to the top.\n\n## Import\n\n```ts\nimport { MScrollTop } from 'morya-ui'\n```\n\n## Basic\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollTop } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem;
|
|
16113
|
+
"markdown": "---\ntitle: ScrollTop\ncategory: 04 / NAVIGATION\ndescription: Shows a back-to-top button after scrolling past a threshold.\n---\n\n# ScrollTop\n\nListens to window or parent scroll and jumps back to the top.\n\n## Import\n\n```ts\nimport { MScrollTop } from 'morya-ui'\n```\n\n## Basic\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MScrollTop, MScrollbar } from 'morya-ui'\n</script>\n\n<template>\n <div style=\"height: 8rem; position: relative\">\n <MScrollbar height=\"8rem\">\n <div style=\"height: 40rem; padding: var(--m-space-3)\">\n Scroll down…\n </div>\n <MScrollTop :threshold=\"80\" target=\"parent\" :right=\"16\" :bottom=\"16\" />\n </MScrollbar>\n </div>\n</template>\n```\n\n## With MScrollbar\n\nWhen `target=\"parent\"`, ScrollTop walks up the DOM for the nearest `.m-scrollbar__wrap` (or native overflow container). Place `MScrollTop` inside the `MScrollbar` default slot and keep `teleport` enabled (default) so the button stays pinned to the viewport corner.\n\n## Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `threshold` | `number` | `400` | Show threshold in px. |\n| `target` | `'window' \\| 'parent'` | `'window'` | Scroll target. |\n| `right` | `string \\| number` | — | Distance from the right edge; a number is pixels. |\n| `bottom` | `string \\| number` | — | Distance from the bottom edge; a number is pixels. |\n| `teleport` | `boolean` | `true` | Overlay Teleport. Mounts to `body` by default. |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Mount target. `'self'` / `false` renders in place. |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | Pass-through; see [Styling & attrs](/docs/attrs). |\n\n\n## Events\n\nNo custom events.\n\n## Slots\n\nNo slots.\n"
|
|
16084
16114
|
}
|
|
16085
16115
|
}
|
|
16086
16116
|
},
|
|
@@ -21348,6 +21378,11 @@
|
|
|
21348
21378
|
"title": "弹出模式",
|
|
21349
21379
|
"body": "`popup` + `v-model` 控制浮层显隐,适合工具栏按钮触发:\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MTieredMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [{ label: '复制' }, { label: '粘贴' }]\n</script>\n\n<template>\n <MButton label=\"操作\" @click=\"open = true\" />\n <MTieredMenu v-model=\"open\" popup :model=\"model\" />\n</template>\n```"
|
|
21350
21380
|
},
|
|
21381
|
+
{
|
|
21382
|
+
"id": "滚动与子菜单",
|
|
21383
|
+
"title": "滚动与子菜单",
|
|
21384
|
+
"body": "主列表与侧向子菜单使用内置 `MScrollbar`(`max-height: min(18rem, 45vh)`)。子菜单 Teleport 到 `body` 相对触发项定位,长列表滚动时不会被主面板裁剪。"
|
|
21385
|
+
},
|
|
21351
21386
|
{
|
|
21352
21387
|
"id": "菜单项结构",
|
|
21353
21388
|
"title": "菜单项结构",
|
|
@@ -21374,7 +21409,7 @@
|
|
|
21374
21409
|
"body": "<h4 id=\"TieredMenuItem\">TieredMenuItem</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ninterface TieredMenuItem {\n label?: string\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: TieredMenuItem[]\n}\n```"
|
|
21375
21410
|
}
|
|
21376
21411
|
],
|
|
21377
|
-
"markdown": "---\ntitle: TieredMenu\ncategory: 04 / NAVIGATION\ndescription: 带一层子菜单的垂直分层菜单。\n---\n\n# TieredMenu\n\n垂直菜单,支持悬停或点击展开**一层**子菜单。`popup` 模式下可作为浮层弹出。\n\n## 引入\n\n```ts\nimport { MTieredMenu, type TieredMenuItem } from 'morya-ui'\n```\n\n## 基础用法\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MTieredMenu } from 'morya-ui'\n\nconst model = [\n {\n label: '文件',\n items: [\n { label: '新建', command: () => window.alert('新建') },\n { label: '导出' },\n ],\n },\n { separator: true },\n { label: '帮助' },\n]\n</script>\n\n<template>\n <MTieredMenu :model=\"model\" />\n</template>\n```\n\n## 弹出模式\n\n`popup` + `v-model` 控制浮层显隐,适合工具栏按钮触发:\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MTieredMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [{ label: '复制' }, { label: '粘贴' }]\n</script>\n\n<template>\n <MButton label=\"操作\" @click=\"open = true\" />\n <MTieredMenu v-model=\"open\" popup :model=\"model\" />\n</template>\n```\n\n## 菜单项结构\n\n| 字段 | 类型 | 说明 |\n| --- | --- | --- |\n| `label` | `string` | 展示文案。 |\n| `command` | `() => void` | 点击叶子项时执行。 |\n| `disabled` | `boolean` | 禁用项。 |\n| `separator` | `boolean` | 渲染分隔线(忽略其他字段)。 |\n| `items` | `TieredMenuItem[]` | 一层子菜单。 |\n\n## Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `model` | `TieredMenuItem[]` | — | 菜单项。 |\n| `popup` | `boolean` | `false` | 弹出模式。 |\n| `modelValue` | `boolean` | `false` | popup 可见性(`v-model`)。 |\n| `teleport` | `boolean` | `true` | popup 时 Teleport;默认 `body`。 |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | 挂载目标。 |\n| `placement` | `FloatingOverlayPlacement` | — | — |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | DOM 透传,见 [样式与 attrs](/docs/attrs). |\n\n\n## Events\n\n| 事件名 | 参数 | 说明 |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | popup 可见性变化。 |\n\n## Slots\n\n无插槽。\n\n## 类型\n\n<h4 id=\"TieredMenuItem\">TieredMenuItem</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ninterface TieredMenuItem {\n label?: string\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: TieredMenuItem[]\n}\n```\n"
|
|
21412
|
+
"markdown": "---\ntitle: TieredMenu\ncategory: 04 / NAVIGATION\ndescription: 带一层子菜单的垂直分层菜单。\n---\n\n# TieredMenu\n\n垂直菜单,支持悬停或点击展开**一层**子菜单。`popup` 模式下可作为浮层弹出。\n\n## 引入\n\n```ts\nimport { MTieredMenu, type TieredMenuItem } from 'morya-ui'\n```\n\n## 基础用法\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MTieredMenu } from 'morya-ui'\n\nconst model = [\n {\n label: '文件',\n items: [\n { label: '新建', command: () => window.alert('新建') },\n { label: '导出' },\n ],\n },\n { separator: true },\n { label: '帮助' },\n]\n</script>\n\n<template>\n <MTieredMenu :model=\"model\" />\n</template>\n```\n\n## 弹出模式\n\n`popup` + `v-model` 控制浮层显隐,适合工具栏按钮触发:\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MTieredMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [{ label: '复制' }, { label: '粘贴' }]\n</script>\n\n<template>\n <MButton label=\"操作\" @click=\"open = true\" />\n <MTieredMenu v-model=\"open\" popup :model=\"model\" />\n</template>\n```\n\n## 滚动与子菜单\n\n主列表与侧向子菜单使用内置 `MScrollbar`(`max-height: min(18rem, 45vh)`)。子菜单 Teleport 到 `body` 相对触发项定位,长列表滚动时不会被主面板裁剪。\n\n## 菜单项结构\n\n| 字段 | 类型 | 说明 |\n| --- | --- | --- |\n| `label` | `string` | 展示文案。 |\n| `command` | `() => void` | 点击叶子项时执行。 |\n| `disabled` | `boolean` | 禁用项。 |\n| `separator` | `boolean` | 渲染分隔线(忽略其他字段)。 |\n| `items` | `TieredMenuItem[]` | 一层子菜单。 |\n\n## Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `model` | `TieredMenuItem[]` | — | 菜单项。 |\n| `popup` | `boolean` | `false` | 弹出模式。 |\n| `modelValue` | `boolean` | `false` | popup 可见性(`v-model`)。 |\n| `teleport` | `boolean` | `true` | popup 时 Teleport;默认 `body`。 |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | 挂载目标。 |\n| `placement` | `FloatingOverlayPlacement` | — | — |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | DOM 透传,见 [样式与 attrs](/docs/attrs). |\n\n\n## Events\n\n| 事件名 | 参数 | 说明 |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | popup 可见性变化。 |\n\n## Slots\n\n无插槽。\n\n## 类型\n\n<h4 id=\"TieredMenuItem\">TieredMenuItem</h4>\n\n完整定义见源码 `types.ts`。\n\n```ts\ninterface TieredMenuItem {\n label?: string\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: TieredMenuItem[]\n}\n```\n"
|
|
21378
21413
|
},
|
|
21379
21414
|
"en-US": {
|
|
21380
21415
|
"title": "TieredMenu",
|
|
@@ -21400,6 +21435,11 @@
|
|
|
21400
21435
|
"title": "Popup mode",
|
|
21401
21436
|
"body": "Combine `popup` with `v-model` for toolbar triggers:\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MTieredMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [{ label: 'Copy' }, { label: 'Paste' }]\n</script>\n\n<template>\n <MButton label=\"Actions\" @click=\"open = true\" />\n <MTieredMenu v-model=\"open\" popup :model=\"model\" />\n</template>\n```"
|
|
21402
21437
|
},
|
|
21438
|
+
{
|
|
21439
|
+
"id": "scrolling-submenu-flyout",
|
|
21440
|
+
"title": "Scrolling & submenu flyout",
|
|
21441
|
+
"body": "The root list and side submenu use built-in `MScrollbar` (`max-height: min(18rem, 45vh)`). Submenus teleport to `body` and anchor to the trigger so they stay visible when the root menu scrolls."
|
|
21442
|
+
},
|
|
21403
21443
|
{
|
|
21404
21444
|
"id": "item-shape",
|
|
21405
21445
|
"title": "Item shape",
|
|
@@ -21426,7 +21466,7 @@
|
|
|
21426
21466
|
"body": "<h4 id=\"TieredMenuItem\">TieredMenuItem</h4>\n\nSee source `types.ts` for the full definition.\n\n```ts\ninterface TieredMenuItem {\n label?: string\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: TieredMenuItem[]\n}\n```"
|
|
21427
21467
|
}
|
|
21428
21468
|
],
|
|
21429
|
-
"markdown": "---\ntitle: TieredMenu\ncategory: 04 / NAVIGATION\ndescription: A vertical layered menu with one submenu level.\n---\n\n# TieredMenu\n\nVertical menu with **one** submenu level on hover or click. Use `popup` for overlay mode.\n\n## Import\n\n```ts\nimport { MTieredMenu, type TieredMenuItem } from 'morya-ui'\n```\n\n## Basic usage\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MTieredMenu } from 'morya-ui'\n\nconst model = [\n {\n label: 'File',\n items: [\n { label: 'New', command: () => window.alert('New') },\n { label: 'Export' },\n ],\n },\n { separator: true },\n { label: 'Help' },\n]\n</script>\n\n<template>\n <MTieredMenu :model=\"model\" />\n</template>\n```\n\n## Popup mode\n\nCombine `popup` with `v-model` for toolbar triggers:\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MTieredMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [{ label: 'Copy' }, { label: 'Paste' }]\n</script>\n\n<template>\n <MButton label=\"Actions\" @click=\"open = true\" />\n <MTieredMenu v-model=\"open\" popup :model=\"model\" />\n</template>\n```\n\n## Item shape\n\n| Field | Type | Description |\n| --- | --- | --- |\n| `label` | `string` | Display text. |\n| `command` | `() => void` | Runs when a leaf item is activated. |\n| `disabled` | `boolean` | Disabled item. |\n| `separator` | `boolean` | Renders a divider (ignores other fields). |\n| `items` | `TieredMenuItem[]` | One submenu level. |\n\n## Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `model` | `TieredMenuItem[]` | — | Menu items. |\n| `popup` | `boolean` | `false` | Popup overlay mode. |\n| `modelValue` | `boolean` | `false` | Popup visibility (`v-model`). |\n| `teleport` | `boolean` | `true` | Teleport when `popup`; defaults to `body`. |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Mount target. |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | Pass-through; see [Styling & attrs](/docs/attrs). |\n\n\n## Events\n\n| Event | Payload | Description |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | Popup visibility change. |\n\n## Slots\n\nNo slots.\n\n## Types\n\n<h4 id=\"TieredMenuItem\">TieredMenuItem</h4>\n\nSee source `types.ts` for the full definition.\n\n```ts\ninterface TieredMenuItem {\n label?: string\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: TieredMenuItem[]\n}\n```\n"
|
|
21469
|
+
"markdown": "---\ntitle: TieredMenu\ncategory: 04 / NAVIGATION\ndescription: A vertical layered menu with one submenu level.\n---\n\n# TieredMenu\n\nVertical menu with **one** submenu level on hover or click. Use `popup` for overlay mode.\n\n## Import\n\n```ts\nimport { MTieredMenu, type TieredMenuItem } from 'morya-ui'\n```\n\n## Basic usage\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MTieredMenu } from 'morya-ui'\n\nconst model = [\n {\n label: 'File',\n items: [\n { label: 'New', command: () => window.alert('New') },\n { label: 'Export' },\n ],\n },\n { separator: true },\n { label: 'Help' },\n]\n</script>\n\n<template>\n <MTieredMenu :model=\"model\" />\n</template>\n```\n\n## Popup mode\n\nCombine `popup` with `v-model` for toolbar triggers:\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MTieredMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [{ label: 'Copy' }, { label: 'Paste' }]\n</script>\n\n<template>\n <MButton label=\"Actions\" @click=\"open = true\" />\n <MTieredMenu v-model=\"open\" popup :model=\"model\" />\n</template>\n```\n\n## Scrolling & submenu flyout\n\nThe root list and side submenu use built-in `MScrollbar` (`max-height: min(18rem, 45vh)`). Submenus teleport to `body` and anchor to the trigger so they stay visible when the root menu scrolls.\n\n## Item shape\n\n| Field | Type | Description |\n| --- | --- | --- |\n| `label` | `string` | Display text. |\n| `command` | `() => void` | Runs when a leaf item is activated. |\n| `disabled` | `boolean` | Disabled item. |\n| `separator` | `boolean` | Renders a divider (ignores other fields). |\n| `items` | `TieredMenuItem[]` | One submenu level. |\n\n## Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `model` | `TieredMenuItem[]` | — | Menu items. |\n| `popup` | `boolean` | `false` | Popup overlay mode. |\n| `modelValue` | `boolean` | `false` | Popup visibility (`v-model`). |\n| `teleport` | `boolean` | `true` | Teleport when `popup`; defaults to `body`. |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Mount target. |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | Pass-through; see [Styling & attrs](/docs/attrs). |\n\n\n## Events\n\n| Event | Payload | Description |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | Popup visibility change. |\n\n## Slots\n\nNo slots.\n\n## Types\n\n<h4 id=\"TieredMenuItem\">TieredMenuItem</h4>\n\nSee source `types.ts` for the full definition.\n\n```ts\ninterface TieredMenuItem {\n label?: string\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: TieredMenuItem[]\n}\n```\n"
|
|
21430
21470
|
}
|
|
21431
21471
|
}
|
|
21432
21472
|
},
|