From 35716f83868f07de8a29c38c530ac48a5a3e0c4d Mon Sep 17 00:00:00 2001 From: chenxi-20 <2465950588@qq.com> Date: Mon, 20 Jan 2025 04:38:19 -0800 Subject: [PATCH 1/4] feat(dropdown): [dropdown] Add visible attribute, support user-defined panel display and hide (#2827) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(dropdown): [dropdown] 增加visible属性,支持用户自定义面板显隐 * feat(dropdown): [dropdown] 增加visible属性,支持用户自定义面板显隐 * feat(dropdown): [dropdown] 补充api版本号 --- examples/sites/demos/apis/dropdown.js | 14 +++++ .../app/dropdown/visible-composition-api.vue | 48 ++++++++++++++ .../demos/pc/app/dropdown/visible.spec.ts | 27 ++++++++ .../sites/demos/pc/app/dropdown/visible.vue | 61 ++++++++++++++++++ .../demos/pc/app/dropdown/webdoc/dropdown.js | 13 ++++ packages/renderless/package.json | 4 +- packages/renderless/src/dropdown/index.ts | 63 ++++++++++++------- packages/renderless/src/dropdown/vue.ts | 15 +++-- packages/renderless/types/dropdown.type.ts | 4 +- packages/vue/src/dropdown/package.json | 2 +- packages/vue/src/dropdown/src/index.ts | 5 ++ packages/vue/src/dropdown/src/pc.vue | 14 ++++- 12 files changed, 235 insertions(+), 35 deletions(-) create mode 100644 examples/sites/demos/pc/app/dropdown/visible-composition-api.vue create mode 100644 examples/sites/demos/pc/app/dropdown/visible.spec.ts create mode 100644 examples/sites/demos/pc/app/dropdown/visible.vue diff --git a/examples/sites/demos/apis/dropdown.js b/examples/sites/demos/apis/dropdown.js index 7bd22cd915..618fc7b248 100644 --- a/examples/sites/demos/apis/dropdown.js +++ b/examples/sites/demos/apis/dropdown.js @@ -218,6 +218,20 @@ export default { pcDemo: 'split-button', mfDemo: '' }, + { + name: 'v-model:visible', + type: 'boolean', + defaultValue: 'false', + meta: { + stable: '3.21.1' + }, + desc: { + 'zh-CN': '手动控制下拉弹框显隐,优先级高于trigger', + 'en-US': 'Manually control the display and hide of the dropdown menu, with priority higher than the trigger' + }, + mode: ['pc'], + pcDemo: 'visible' + }, { name: 'visible-arrow', type: 'boolean', diff --git a/examples/sites/demos/pc/app/dropdown/visible-composition-api.vue b/examples/sites/demos/pc/app/dropdown/visible-composition-api.vue new file mode 100644 index 0000000000..408d9b52bb --- /dev/null +++ b/examples/sites/demos/pc/app/dropdown/visible-composition-api.vue @@ -0,0 +1,48 @@ + + + diff --git a/examples/sites/demos/pc/app/dropdown/visible.spec.ts b/examples/sites/demos/pc/app/dropdown/visible.spec.ts new file mode 100644 index 0000000000..49843c032c --- /dev/null +++ b/examples/sites/demos/pc/app/dropdown/visible.spec.ts @@ -0,0 +1,27 @@ +import { test, expect } from '@playwright/test' + +test('手动控制显隐', async ({ page }) => { + page.on('pageerror', (exception) => expect(exception).toBeNull()) + await page.goto('dropdown#visible') + + const wrap = page.locator('#visible') + const dropDownMenu = page.locator('body > .tiny-dropdown-menu').locator('visible=true') + + await wrap.getByText('点击显示').click() + await expect(dropDownMenu).toHaveCount(1) + + await page.locator('#all-demos-container').click() + await expect(dropDownMenu).toHaveCount(1) + + await dropDownMenu.locator('div').filter({ hasText: '黄金糕' }).nth(1).click() + await expect(dropDownMenu).toHaveCount(1) + + await wrap.getByText('点击隐藏').click() + await expect(dropDownMenu).toHaveCount(0) + + await wrap.getByText('点击显示').click() + await expect(dropDownMenu).toHaveCount(1) + + await dropDownMenu.locator('div').filter({ hasText: '点击我隐藏' }).nth(1).click() + await expect(dropDownMenu).toHaveCount(0) +}) diff --git a/examples/sites/demos/pc/app/dropdown/visible.vue b/examples/sites/demos/pc/app/dropdown/visible.vue new file mode 100644 index 0000000000..51ef2f9efe --- /dev/null +++ b/examples/sites/demos/pc/app/dropdown/visible.vue @@ -0,0 +1,61 @@ + + + diff --git a/examples/sites/demos/pc/app/dropdown/webdoc/dropdown.js b/examples/sites/demos/pc/app/dropdown/webdoc/dropdown.js index 38ef6286ce..071b03632d 100644 --- a/examples/sites/demos/pc/app/dropdown/webdoc/dropdown.js +++ b/examples/sites/demos/pc/app/dropdown/webdoc/dropdown.js @@ -111,6 +111,19 @@ export default { }, codeFiles: ['trigger.vue'] }, + { + demoId: 'visible', + name: { + 'zh-CN': '手动控制显隐', + 'en-US': 'Manual control of display and concealment' + }, + desc: { + 'zh-CN': '

通过 visible 属性手动控制下拉菜单显隐,优先级高于trigger。

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

Manually control the visibility of the dropdown menu through thevisibleattribute, with priority over trigger.

\n' + }, + codeFiles: ['visible.vue'] + }, { demoId: 'tip', name: { diff --git a/packages/renderless/package.json b/packages/renderless/package.json index 419ada67a0..095593d64a 100644 --- a/packages/renderless/package.json +++ b/packages/renderless/package.json @@ -1,6 +1,6 @@ { "name": "@opentiny/vue-renderless", - "version": "3.21.0", + "version": "3.21.1", "private": true, "description": "An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.", "author": "OpenTiny Team", @@ -43,4 +43,4 @@ "esno": "^4.7.0", "tsup": "7.2.0" } -} \ No newline at end of file +} diff --git a/packages/renderless/src/dropdown/index.ts b/packages/renderless/src/dropdown/index.ts index 3dfa5aa354..4b7940e0e4 100644 --- a/packages/renderless/src/dropdown/index.ts +++ b/packages/renderless/src/dropdown/index.ts @@ -39,24 +39,28 @@ export const watchFocusing = (parent: IDropdownRenderlessParams['parent']) => (v } export const show = - ({ props, state }: Pick) => + ({ props, state, emit }: Pick) => () => { if (props.disabled) { return } - clearTimeout(Number(state.timeout)) - - state.timeout = setTimeout( - () => { - state.visible = true - }, - state.trigger === 'click' ? 0 : props.showTimeout - ) + if (state.visibleIsBoolean) { + emit('update:visible', true) + } else { + clearTimeout(Number(state.timeout)) + + state.timeout = setTimeout( + () => { + state.visible = true + }, + state.trigger === 'click' ? 0 : props.showTimeout + ) + } } export const hide = - ({ api, props, state }: Pick) => + ({ api, props, state, emit }: Pick) => () => { if (props.disabled) { return @@ -68,14 +72,18 @@ export const hide = api.resetTabindex(state.triggerElm) } - clearTimeout(Number(state.timeout)) - - state.timeout = setTimeout( - () => { - state.visible = false - }, - state.trigger === 'click' ? 0 : props.hideTimeout - ) + if (state.visibleIsBoolean) { + emit('update:visible', false) + } else { + clearTimeout(Number(state.timeout)) + + state.timeout = setTimeout( + () => { + state.visible = false + }, + state.trigger === 'click' ? 0 : props.hideTimeout + ) + } } export const handleClick = @@ -85,9 +93,12 @@ export const handleClick = return } - emit('handle-click', state.visible) - - state.visible ? api.hide() : api.show() + if (state.visibleIsBoolean) { + emit('handle-click', props.visible) + } else { + emit('handle-click', state.visible) + state.visible ? api.hide() : api.show() + } } export const handleTriggerKeyDown = @@ -112,7 +123,7 @@ export const handleTriggerKeyDown = } export const handleItemKeyDown = - ({ api, props, state }: Pick) => + ({ api, props, state, emit }: Pick) => (event: KeyboardEvent) => { const keyCode = event.keyCode const target = event.target @@ -199,6 +210,10 @@ export const initEvent = on(state.triggerElm, 'click', api.toggleFocusOnFalse) } + if (state.visibleIsBoolean) { + return + } + if (state.trigger === 'hover') { on(state.triggerElm, 'mouseenter', api.show) on(state.triggerElm, 'mouseleave', api.hide) @@ -262,7 +277,9 @@ export const mounted = vm.$on('selected-index', (selectedIndex) => { broadcast('TinyDropdownMenu', 'menu-selected-index', selectedIndex) }) - vm.$on('is-disabled', api.clickOutside) + if (!state.visibleIsBoolean) { + vm.$on('is-disabled', api.clickOutside) + } } export const beforeDistory = diff --git a/packages/renderless/src/dropdown/vue.ts b/packages/renderless/src/dropdown/vue.ts index 009819d742..75a17ed12c 100644 --- a/packages/renderless/src/dropdown/vue.ts +++ b/packages/renderless/src/dropdown/vue.ts @@ -62,7 +62,8 @@ export const renderless = ( designConfig, trigger: computed(() => { return props.trigger || designConfig?.props?.trigger || 'hover' - }) + }), + visibleIsBoolean: computed(() => typeof props.visible === 'boolean') }) provide('dropdownVm', vm) @@ -71,12 +72,12 @@ export const renderless = ( state, watchVisible: watchVisible({ broadcast, emit, nextTick }), watchFocusing: watchFocusing(parent), - show: show({ props, state }), - hide: hide({ api, props, state }), + show: show({ props, state, emit }), + hide: hide({ api, props, state, emit }), mounted: mounted({ api, vm, state, broadcast }), handleClick: handleClick({ api, props, state, emit }), handleTriggerKeyDown: handleTriggerKeyDown({ api, state }), - handleItemKeyDown: handleItemKeyDown({ api, props, state }), + handleItemKeyDown: handleItemKeyDown({ api, props, state, emit }), resetTabindex: resetTabindex(api), removeTabindex: removeTabindex(state), initAria: initAria({ state, props }), @@ -91,7 +92,11 @@ export const renderless = ( toggleFocusOnFalse: toggleFocus({ state, value: false }) }) - watch(() => state.visible, api.watchVisible) + if (typeof props.visible === 'boolean') { + watch(() => props.visible, api.watchVisible) + } else { + watch(() => state.visible, api.watchVisible) + } watch(() => state.focusing, api.watchFocusing) onMounted(api.mounted) diff --git a/packages/renderless/types/dropdown.type.ts b/packages/renderless/types/dropdown.type.ts index 2397fc0eef..bf407cc79e 100644 --- a/packages/renderless/types/dropdown.type.ts +++ b/packages/renderless/types/dropdown.type.ts @@ -20,7 +20,7 @@ export interface IDropdownState { visible: boolean timeout: null | NodeJS.Timeout focusing: false - menuItems: NodeListOf | undefined | null + menuItems: NodeListOf | undefined | null | [] menuItemsArray: HTMLElement[] | null triggerElm: HTMLElement | null dropdownElm: HTMLElement | null @@ -28,6 +28,8 @@ export interface IDropdownState { showIcon: boolean showSelfIcon: boolean designConfig: IDropdownRenderlessParamUtils['designConfig'] + trigger: 'click' | 'hover' + visibleIsBoolean: boolean } export interface IDropdownApi { diff --git a/packages/vue/src/dropdown/package.json b/packages/vue/src/dropdown/package.json index 289567fdce..56f9a64039 100644 --- a/packages/vue/src/dropdown/package.json +++ b/packages/vue/src/dropdown/package.json @@ -1,6 +1,6 @@ { "name": "@opentiny/vue-dropdown", - "version": "3.21.0", + "version": "3.21.1", "description": "", "main": "lib/index.js", "module": "index.ts", diff --git a/packages/vue/src/dropdown/src/index.ts b/packages/vue/src/dropdown/src/index.ts index ea71149d7c..f3e09ee9d9 100644 --- a/packages/vue/src/dropdown/src/index.ts +++ b/packages/vue/src/dropdown/src/index.ts @@ -4,6 +4,11 @@ import template from 'virtual-template?pc|mobile-first' export const dropdownProps = { ...$props, modelValue: [String, Number], + // tiny新增 + visible: { + type: Boolean || undefined, + default: undefined + }, type: String, trigger: String, // 默认主题为 'hover' size: { diff --git a/packages/vue/src/dropdown/src/pc.vue b/packages/vue/src/dropdown/src/pc.vue index cdbd09fa9a..e6f515d02a 100644 --- a/packages/vue/src/dropdown/src/pc.vue +++ b/packages/vue/src/dropdown/src/pc.vue @@ -33,6 +33,11 @@ export default defineComponent({ ...$props, type: String, trigger: String, + // tiny新增 + visible: { + type: Boolean || undefined, + default: undefined + }, size: { type: String, default: '' @@ -101,7 +106,8 @@ export default defineComponent({ 'menu-item-click', 'handle-click', 'is-disabled', - 'selected-index' + 'selected-index', + 'update:visible' ], setup(props, context) { return setup({ props, context, renderless, api, h }) @@ -189,7 +195,9 @@ export default defineComponent({ ) : ( + class={`is-text${state.visible ? ' is-expand' : ' is-hide'}${ + disabled ? ' is-disabled' : '' + } ${triggerClass}`}> {prefixInner} {defaultTriggerElm} {suffixInner} @@ -208,7 +216,7 @@ export default defineComponent({ const menuElm = disabled ? null : (slots.dropdown && slots.dropdown()) || defaulMenuElm return ( -
+
{triggerElm} {menuElm}
From 2bf2a7840f231a9037ed0d3ba7040554249f6088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B3=E5=90=9B=E5=81=A5?= <40288193@qq.com> Date: Wed, 22 Jan 2025 16:23:13 +0800 Subject: [PATCH 2/4] fix(tooltip): remove :has selector on arrow (#2830) --- packages/theme/package.json | 4 ++-- packages/theme/src/base/reset.less | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/theme/package.json b/packages/theme/package.json index 0204756a49..657cb46645 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,7 +1,7 @@ { "name": "@opentiny/vue-theme", "type": "module", - "version": "3.21.1", + "version": "3.21.2", "description": "An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.", "author": "OpenTiny Team", "license": "MIT", @@ -93,4 +93,4 @@ ] } } -} +} \ No newline at end of file diff --git a/packages/theme/src/base/reset.less b/packages/theme/src/base/reset.less index eb237cdff0..feb297ea19 100644 --- a/packages/theme/src/base/reset.less +++ b/packages/theme/src/base/reset.less @@ -197,9 +197,9 @@ } // 有箭头的场景,统一规范所有样式 -.tiny-popconfirm-popover:has(.popper__arrow), -.tiny-popper:has(.popper__arrow), -.tiny-tooltip__popper:has(.popper__arrow) { +.tiny-popconfirm-popover, +.tiny-popper, +.tiny-tooltip__popper { .popper__arrow { position: absolute; display: block; @@ -243,4 +243,4 @@ right: -3px; } } -} +} \ No newline at end of file From b5855ee7a9f2e1953953e1b9ccf20e4008cd6d37 Mon Sep 17 00:00:00 2001 From: gimmyhehe <975402925@qq.com> Date: Wed, 22 Jan 2025 16:54:23 +0800 Subject: [PATCH 3/4] fix(grid): [grid] fix footer bug in visual scroll scene (#2839) * fix: [grid] fix footer bug in visual scroll scene * test: fix e2e test case --- .../demos/pc/app/grid/editor/custom-edit.spec.js | 4 ++-- packages/vue/src/grid/package.json | 2 +- packages/vue/src/grid/src/body/src/body.tsx | 14 +++++++------- packages/vue/src/grid/src/table/src/methods.ts | 6 ++++-- .../src/grid/src/table/src/utils/updateStyle.ts | 7 +++++++ 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/examples/sites/demos/pc/app/grid/editor/custom-edit.spec.js b/examples/sites/demos/pc/app/grid/editor/custom-edit.spec.js index 8e740e46b6..59a7a634aa 100644 --- a/examples/sites/demos/pc/app/grid/editor/custom-edit.spec.js +++ b/examples/sites/demos/pc/app/grid/editor/custom-edit.spec.js @@ -3,6 +3,6 @@ import { test, expect } from '@playwright/test' test('多行编辑', async ({ page }) => { page.on('pageerror', (exception) => expect(exception).toBeNull()) await page.goto('grid-editor#editor-custom-edit') - await expect(page.getByRole('row', { name: '1 请选择 保存 取消' }).getByRole('textbox').first()).toBeVisible() - await expect(page.getByRole('row', { name: '2 请选择 保存 取消' }).getByRole('textbox').first()).toBeVisible() + await expect(page.getByRole('cell', { name: 'GFD 科技有限公司' }).getByRole('textbox')).toBeVisible() + await expect(page.getByRole('cell', { name: 'WWWW 科技有限公司' }).getByRole('textbox')).toBeVisible() }) diff --git a/packages/vue/src/grid/package.json b/packages/vue/src/grid/package.json index 3fbabf144d..e2b2a44744 100644 --- a/packages/vue/src/grid/package.json +++ b/packages/vue/src/grid/package.json @@ -1,7 +1,7 @@ { "name": "@opentiny/vue-grid", "type": "module", - "version": "3.21.1", + "version": "3.21.2", "description": "", "license": "MIT", "sideEffects": false, diff --git a/packages/vue/src/grid/src/body/src/body.tsx b/packages/vue/src/grid/src/body/src/body.tsx index c6b7ec9680..9362579251 100644 --- a/packages/vue/src/grid/src/body/src/body.tsx +++ b/packages/vue/src/grid/src/body/src/body.tsx @@ -751,11 +751,12 @@ function renderDefEmpty(h) { } const syncHeaderAndFooterScroll = ({ bodyElem, footerElem, headerElem, isX }) => { + const scrollLeft = bodyElem.scrollLeft if (isX && headerElem) { - headerElem.scrollLeft = bodyElem.scrollLeft + headerElem.scrollLeft = scrollLeft } if (isX && footerElem) { - footerElem.scrollLeft = bodyElem.scrollLeft + footerElem.scrollLeft = scrollLeft } } @@ -870,10 +871,6 @@ export default defineComponent({ // 空数据元素 elemStore[`${keyPrefix}emptyBlock`] = $refs.emptyBlock - // 表体第一层div监听滚动事件 - $el.onscroll = this.scrollEvent - $el._onscroll = this.scrollEvent - if (dropConfig) { const { plugin, row = true } = dropConfig plugin && row && (this.rowSortable = $table.rowDrop(this.$el)) @@ -907,7 +904,10 @@ export default defineComponent({ 'div', { ref: 'body', - class: ['tiny-grid__body-wrapper', 'body__wrapper', { [classMap.isScrollload]: scrollLoad }] + class: ['tiny-grid__body-wrapper', 'body__wrapper', { [classMap.isScrollload]: scrollLoad }], + on: { + scroll: this.scrollEvent + } }, [ // 表格主体内容x轴方向虚拟滚动条占位元素 diff --git a/packages/vue/src/grid/src/table/src/methods.ts b/packages/vue/src/grid/src/table/src/methods.ts index b97546746d..6f6a16dd78 100644 --- a/packages/vue/src/grid/src/table/src/methods.ts +++ b/packages/vue/src/grid/src/table/src/methods.ts @@ -1644,8 +1644,10 @@ const Methods = { // 设置新的渲染列触发Vue渲染 this.tableColumn = ret.tableColumn this.visibleColumnChanged = ret.visibleColumnChanged - - this.$nextTick(this.updateStyle) + this.$nextTick(() => { + this.updateFooter() + this.updateStyle() + }) }) }, // 更新横向 X 可视渲染上下剩余空间大小 diff --git a/packages/vue/src/grid/src/table/src/utils/updateStyle.ts b/packages/vue/src/grid/src/table/src/utils/updateStyle.ts index eb2d7af0f1..f9d9e513a0 100644 --- a/packages/vue/src/grid/src/table/src/utils/updateStyle.ts +++ b/packages/vue/src/grid/src/table/src/utils/updateStyle.ts @@ -44,6 +44,7 @@ function getTableWidth({ scrollXLoad, tWidth, tableColumn }) { } function layoutFooter({ + elemStore, customHeight, footerHeight, headerHeight, @@ -61,6 +62,11 @@ function layoutFooter({ let tWidth = tableWidth // 如果是固定列与设置了超出隐藏 let ret = getTableWidth({ scrollXLoad, tWidth, tableColumn }) + // 为表尾设置虚拟滚动占位宽度 + const spaceElem = elemStore['main-footer-x-space'] + if (spaceElem) { + spaceElem.style.width = `${tableWidth}px` + } tableColumn = ret.tableColumn tWidth = ret.tWidth @@ -242,6 +248,7 @@ export function handleLayout(params) { tableColumn = ret.tableColumn } else if (layout === 'footer') { tableColumn = layoutFooter({ + elemStore, customHeight, fixedWrapperElem, footerHeight, From 824ec5257ba703f7acdf1acaf1836e7f12118ed0 Mon Sep 17 00:00:00 2001 From: ajaxzheng <894103554@qq.com> Date: Wed, 22 Jan 2025 16:57:30 +0800 Subject: [PATCH 4/4] chore: update @opentiny/vue version --- packages/vue/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vue/package.json b/packages/vue/package.json index dadf75dfea..96b77bfb65 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,7 +1,7 @@ { "name": "@opentiny/vue", "private": true, - "version": "3.21.1", + "version": "3.21.2", "description": "An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.", "homepage": "https://opentiny.design/tiny-vue", "keywords": [ @@ -275,4 +275,4 @@ "build": "pnpm -w build:ui", "postversion": "pnpm build" } -} \ No newline at end of file +}