From fdbfc681d70546891a29ba4a8020b4913cb63205 Mon Sep 17 00:00:00 2001 From: jieliu52 Date: Thu, 13 Mar 2025 18:52:15 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(tree-menu):=20=E6=94=B6=E8=B5=B7?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E6=97=B6=E6=94=AF=E6=8C=81=E6=82=AC=E6=B5=AE?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=AD=90=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/sites/demos/apis/tree-menu.js | 12 ++ .../pop-sub-menu-composition-api.vue | 123 ++++++++++++++++ .../pc/app/tree-menu/pop-sub-menu.spec.ts | 26 ++++ .../demos/pc/app/tree-menu/pop-sub-menu.vue | 132 ++++++++++++++++++ .../pc/app/tree-menu/webdoc/tree-menu.js | 12 ++ packages/renderless/src/tree-menu/vue.ts | 4 +- packages/theme/src/tree-menu/index.less | 73 ++++++++++ packages/theme/src/tree-menu/vars.less | 25 ++++ packages/vue/src/tree-menu/src/menu-node.vue | 50 +++++++ packages/vue/src/tree-menu/src/pc.vue | 37 +++-- packages/vue/src/tree-menu/src/pop-menu.vue | 109 +++++++++++++++ packages/vue/src/tree-menu/src/props.ts | 4 + 12 files changed, 595 insertions(+), 12 deletions(-) create mode 100644 examples/sites/demos/pc/app/tree-menu/pop-sub-menu-composition-api.vue create mode 100644 examples/sites/demos/pc/app/tree-menu/pop-sub-menu.spec.ts create mode 100644 examples/sites/demos/pc/app/tree-menu/pop-sub-menu.vue create mode 100644 packages/vue/src/tree-menu/src/menu-node.vue create mode 100644 packages/vue/src/tree-menu/src/pop-menu.vue diff --git a/examples/sites/demos/apis/tree-menu.js b/examples/sites/demos/apis/tree-menu.js index eb36cf68fb..dd1522264b 100644 --- a/examples/sites/demos/apis/tree-menu.js +++ b/examples/sites/demos/apis/tree-menu.js @@ -342,6 +342,18 @@ export default { mode: ['pc'], pcDemo: 'show-expand' }, + { + name: 'expand-menu-popable', + type: 'boolean', + defaultValue: 'false', + desc: { + 'zh-CN': '启用一键展开/收起功能下。是否支持悬浮展示子菜单', + 'en-US': + 'when the one click expand/collapse function enabled. whether to support hovering to display submenus' + }, + mode: ['pc'], + pcDemo: 'pop-sub-menu' + }, { name: 'show-filter', type: 'boolean', diff --git a/examples/sites/demos/pc/app/tree-menu/pop-sub-menu-composition-api.vue b/examples/sites/demos/pc/app/tree-menu/pop-sub-menu-composition-api.vue new file mode 100644 index 0000000000..63c1d609c8 --- /dev/null +++ b/examples/sites/demos/pc/app/tree-menu/pop-sub-menu-composition-api.vue @@ -0,0 +1,123 @@ + + + diff --git a/examples/sites/demos/pc/app/tree-menu/pop-sub-menu.spec.ts b/examples/sites/demos/pc/app/tree-menu/pop-sub-menu.spec.ts new file mode 100644 index 0000000000..40887a76b1 --- /dev/null +++ b/examples/sites/demos/pc/app/tree-menu/pop-sub-menu.spec.ts @@ -0,0 +1,26 @@ +import { test, expect } from '@playwright/test' + +test('静态数据', async ({ page }) => { + page.on('pageerror', (exception) => expect(exception).toBeNull()) + await page.goto('tree-menu#basic-usage') + + const wrap = page.locator('#basic-usage') + const treeMenu = wrap.locator('.tiny-tree-menu') + const treeNode = treeMenu.locator('.tiny-tree-node__wrapper > .tiny-tree-node') + const treeNodeContent = treeNode.locator('> .tiny-tree-node__content') + + await expect(treeNode.filter({ hasText: /^环境准备$/ })).toBeHidden() + await treeNodeContent.filter({ hasText: /^使用指南$/ }).click() + await expect(treeNode.filter({ hasText: /^环境准备$/ })).toBeVisible() + await treeNode.filter({ hasText: /^环境准备$/ }).click() + await expect(treeNode.filter({ hasText: /^环境准备$/ })).toHaveClass(/is-current/) + await treeNodeContent.filter({ hasText: /^使用指南$/ }).click() + await expect(treeNode.filter({ hasText: /^环境准备$/ })).toBeHidden() + + // 过滤功能 + await treeMenu.locator('.tiny-input__inner').fill('新增组件') + await expect(page.getByTitle('新增组件')).toBeVisible() + await expect(treeNodeContent.filter({ hasText: /^使用指南$/ })).toBeHidden() + await treeMenu.locator('.tiny-input__inner').clear() + await expect(treeNodeContent.filter({ hasText: /^使用指南$/ })).toBeVisible() +}) diff --git a/examples/sites/demos/pc/app/tree-menu/pop-sub-menu.vue b/examples/sites/demos/pc/app/tree-menu/pop-sub-menu.vue new file mode 100644 index 0000000000..9ff8517deb --- /dev/null +++ b/examples/sites/demos/pc/app/tree-menu/pop-sub-menu.vue @@ -0,0 +1,132 @@ + + + diff --git a/examples/sites/demos/pc/app/tree-menu/webdoc/tree-menu.js b/examples/sites/demos/pc/app/tree-menu/webdoc/tree-menu.js index d3ed3963bc..2994386b99 100644 --- a/examples/sites/demos/pc/app/tree-menu/webdoc/tree-menu.js +++ b/examples/sites/demos/pc/app/tree-menu/webdoc/tree-menu.js @@ -148,6 +148,18 @@ export default { }, codeFiles: ['show-expand.vue'] }, + { + demoId: 'pop-sub-menu', + name: { + 'zh-CN': '折叠弹出', + 'en-US': 'Pop Sub Menu' + }, + desc: { + 'zh-CN': '

折叠模式下,支持弹出子菜单列表。

\n', + 'en-US': '

support pop sub menus when collapsed.

\n' + }, + codeFiles: ['pop-sub-menu.vue'] + }, { demoId: 'custom-icon', name: { diff --git a/packages/renderless/src/tree-menu/vue.ts b/packages/renderless/src/tree-menu/vue.ts index bdd89a6c6c..3edf0d9829 100644 --- a/packages/renderless/src/tree-menu/vue.ts +++ b/packages/renderless/src/tree-menu/vue.ts @@ -77,7 +77,7 @@ export const api = [ export const renderless = ( props: ITreeMenuProps, - { computed, watch, reactive, onMounted }: ISharedRenderlessFunctionParams, + { computed, watch, reactive, onMounted, provide }: ISharedRenderlessFunctionParams, { t, service, emit, vm }: ISharedRenderlessParamUtils ) => { service = service || { base: {} } @@ -128,6 +128,8 @@ export const renderless = ( computedTreeStyle: computedTreeStyle({ props }) }) + provide('tree-menu', vm) + watch( () => props.data, (value) => (state.data = value), diff --git a/packages/theme/src/tree-menu/index.less b/packages/theme/src/tree-menu/index.less index eb41ce57e4..528d2ff3a8 100644 --- a/packages/theme/src/tree-menu/index.less +++ b/packages/theme/src/tree-menu/index.less @@ -17,6 +17,8 @@ @tree-node-prefix-cls: ~'@{css-prefix}tree-node'; @input-prefix-cls: ~'@{css-prefix}input'; @tree-menu-prefix-cls: ~'@{css-prefix}tree-menu'; +@tree-pop-menu-prefix-cls: ~'@{css-prefix}tree-menu-pop-menu'; +@tree-pop-menu-panel-prefix-cls: ~'@{css-prefix}tree-menu-pop-menu-panel'; .@{tree-menu-prefix-cls} { .inject-TreeMenu-vars(); @@ -212,6 +214,7 @@ .tree-node-name { align-items: center; padding: 0 var(--tv-TreeMenu-node-body-text-padding-x); + display: flex; &:hover { font-weight: var(--tv-TreeMenu-node-name-hover-font-weight); @@ -219,6 +222,7 @@ svg { margin-right: var(--tv-TreeMenu-prefix-icon-margin-right); + flex-shrink: 0; } } } @@ -335,4 +339,73 @@ } } } + + .@{tree-pop-menu-prefix-cls} { + line-height: initial; + } +} + +.@{tree-pop-menu-panel-prefix-cls} { + .inject-TreeMenu-vars(); + padding: var(--tv-TreeMenu-pop-padding) !important; + border-radius: var(--tv-TreeMenu-pop-radius) !important; + box-shadow: var(--tv-TreeMenu-pop-shadow) !important; + width: var(--tv-TreeMenu-pop-width); + transform: translateX(var(--tv-TreeMenu-pop-panel-margin-left)); + + .tree-menu-pop-menu__list { + width: 100%; + + .tiny-tree-menu-pop-menu { + width: 100%; + } + + .reference-wrapper { + display: block; + } + + &-item { + width: 100%; + height: var(--tv-TreeMenu-pop-item-height); + display: flex; + align-items: center; + + .tree-node-name { + svg { + margin-right: var(--tv-TreeMenu-prefix-icon-margin-right); + } + } + + &.hover, + &:hover, + &:active { + background-color: var(--tv-TreeMenu-pop-item-active-bg); + color: var(--tv-TreeMenu-pop-item-active-text-color); + } + + &.has-current { + background-color: var(--tv-TreeMenu-pop-item-active-bg); + color: var(--tv-TreeMenu-pop-item-active-text-color); + } + + &.is-current { + background-color: var(--tv-TreeMenu-pop-item-selected-bg); + color: var(--tv-TreeMenu-pop-item-selected-text-color); + } + + .tree-node { + padding-left: var(--tv-TreeMenu-pop-item-padding-left); + display: flex; + align-items: center; + + &-body { + color: inherit; + } + } + } + } + + &__first { + transform: translateX(var(--tv-TreeMenu-pop-panel-first-margin-left)); + } } diff --git a/packages/theme/src/tree-menu/vars.less b/packages/theme/src/tree-menu/vars.less index fa3a6b2820..72834ab8ce 100644 --- a/packages/theme/src/tree-menu/vars.less +++ b/packages/theme/src/tree-menu/vars.less @@ -77,4 +77,29 @@ --tv-TreeMenu-node-name-hover-font-weight: var(--tv-font-weight-bold); // 左侧折叠图标背景色 --tv-TreeMenu-toggle-button-background-color: var(--tv-color-bg-3); + + // 弹出面板 边距 + --tv-TreeMenu-pop-padding: var(--tv-space-base); + // 弹出面板 圆角 + --tv-TreeMenu-pop-radius: var(--tv-border-radius-sm); + // 弹出面板 圆角 + --tv-TreeMenu-pop-shadow: var(--tv-shadow-4-down); + // 弹出面板 菜单高度 + --tv-TreeMenu-pop-item-height: 36px; + // 弹出面板 宽度 + --tv-TreeMenu-pop-width: 160px; + // 弹出面板 菜单悬浮、激活背景 + --tv-TreeMenu-pop-item-active-bg: transparent; + // 弹出面板 菜单悬浮、激活文本色 + --tv-TreeMenu-pop-item-active-text-color: var(--tv-base-color-brand); + // 弹出面板 选中背景色 + --tv-TreeMenu-pop-item-selected-bg: transparent; + // 弹出面板 选中文本色 + --tv-TreeMenu-pop-item-selected-text-color: var(--tv-base-color-brand); + // 弹出面板 选中文本色 + --tv-TreeMenu-pop-item-padding-left: 16px; + // 弹出面板 左侧边距 + --tv-TreeMenu-pop-panel-margin-left: -2px; + // 弹出面板 第一层左侧边距 + --tv-TreeMenu-pop-panel-first-margin-left: -38px; } diff --git a/packages/vue/src/tree-menu/src/menu-node.vue b/packages/vue/src/tree-menu/src/menu-node.vue new file mode 100644 index 0000000000..f316c86c84 --- /dev/null +++ b/packages/vue/src/tree-menu/src/menu-node.vue @@ -0,0 +1,50 @@ + + + + + + diff --git a/packages/vue/src/tree-menu/src/pc.vue b/packages/vue/src/tree-menu/src/pc.vue index c59cf094dc..4596c578ca 100644 --- a/packages/vue/src/tree-menu/src/pc.vue +++ b/packages/vue/src/tree-menu/src/pc.vue @@ -78,17 +78,26 @@ @current-change="currentChange" >
@@ -105,6 +114,9 @@ import { $prefix, setup, defineComponent } from '@opentiny/vue-common' import { renderless, api } from '@opentiny/vue-renderless/tree-menu/vue' import Tree from '@opentiny/vue-tree' import Input from '@opentiny/vue-input' +import Tooltip from '@opentiny/vue-tooltip' +import PopMenu from './pop-menu.vue' +import MenuNode from './menu-node.vue' import { iconLeftWardArrow, iconEditorMenuLeft, iconEditorMenuRight } from '@opentiny/vue-icon' import { treeMenuProps } from './props' import '@opentiny/vue-theme/tree-menu/index.less' @@ -129,6 +141,9 @@ export default defineComponent({ components: { TinyTree: Tree, TinyInput: Input, + TinyTooltip: Tooltip, + TinyTreeMenuPopMenu: PopMenu, + TinyTreeMenuNode: MenuNode, IconArrow: iconLeftWardArrow(), IconEditorMenuLeft: iconEditorMenuLeft(), IconEditorMenuRight: iconEditorMenuRight() diff --git a/packages/vue/src/tree-menu/src/pop-menu.vue b/packages/vue/src/tree-menu/src/pop-menu.vue new file mode 100644 index 0000000000..45866a130f --- /dev/null +++ b/packages/vue/src/tree-menu/src/pop-menu.vue @@ -0,0 +1,109 @@ + + + + + + diff --git a/packages/vue/src/tree-menu/src/props.ts b/packages/vue/src/tree-menu/src/props.ts index 69f273c953..4943886798 100644 --- a/packages/vue/src/tree-menu/src/props.ts +++ b/packages/vue/src/tree-menu/src/props.ts @@ -67,6 +67,10 @@ export const treeMenuProps = { type: Boolean, default: false }, + expandMenuPopable: { + type: Boolean, + default: false + }, collapsible: { type: Boolean, default: true From 4a5eda3d2b13ba5f21429eaba0e02fb5dca182b1 Mon Sep 17 00:00:00 2001 From: jieliu52 Date: Fri, 14 Mar 2025 08:58:51 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20popover=20=E4=B8=8D=E6=8C=82?= =?UTF-8?q?=E8=BD=BD=E5=88=B0=20body?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/vue/src/tree-menu/src/pop-menu.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vue/src/tree-menu/src/pop-menu.vue b/packages/vue/src/tree-menu/src/pop-menu.vue index 45866a130f..f111f9f719 100644 --- a/packages/vue/src/tree-menu/src/pop-menu.vue +++ b/packages/vue/src/tree-menu/src/pop-menu.vue @@ -19,6 +19,7 @@ :visible-arrow="false" :popperClass="'tiny-tree-menu-pop-menu-panel ' + (node.level === 1 && 'tiny-tree-menu-pop-menu-panel__first')" class="tiny-tree-menu-pop-menu" + :append-to-body="false" > - + diff --git a/packages/vue/src/tree-menu/src/pop-menu.vue b/packages/vue/src/tree-menu/src/pop-menu.vue index f111f9f719..5b9d645e28 100644 --- a/packages/vue/src/tree-menu/src/pop-menu.vue +++ b/packages/vue/src/tree-menu/src/pop-menu.vue @@ -17,9 +17,13 @@ trigger="hover" placement="right" :visible-arrow="false" - :popperClass="'tiny-tree-menu-pop-menu-panel ' + (node.level === 1 && 'tiny-tree-menu-pop-menu-panel__first')" + :popperClass=" + 'tiny-tree-menu-pop-menu-panel ' + + (node.level === 1 && 'tiny-tree-menu-pop-menu-panel__first') + + ' ' + + (popperClass || '') + " class="tiny-tree-menu-pop-menu" - :append-to-body="false" >