Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions examples/sites/demos/apis/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,16 +195,20 @@ export default {
},
{
name: 'trigger',
type: "'hover' | 'click'",
type: "'hover' | 'click' | 'contextmenu'",
defaultValue: "'hover'",
desc: {
'zh-CN': '触发下拉的方式',
'en-US': 'Trigger the dropdown method'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'trigger',
mfDemo: ''
mfDemo: '',
meta: {
stable: '3.28.0'
}
},

{
name: 'type',
typeAnchorName: 'IButtonType',
Expand Down
13 changes: 13 additions & 0 deletions examples/sites/demos/pc/app/dropdown/trigger-composition-api.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@
</tiny-dropdown-menu>
</template>
</tiny-dropdown>

<p>场景 3:设置右键触发下拉</p>
<tiny-dropdown trigger="contextmenu">
<template #dropdown>
<tiny-dropdown-menu>
<tiny-dropdown-item :icon="tinyIconPlus">黄金糕</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconPlusCircle">狮子头</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconPlusSquare">螺蛳粉</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconCheckedLinear">双皮奶</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconCheckedSur">蚵仔煎</tiny-dropdown-item>
</tiny-dropdown-menu>
</template>
</tiny-dropdown>
</div>
</template>

Expand Down
13 changes: 13 additions & 0 deletions examples/sites/demos/pc/app/dropdown/trigger.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ test('触发方式', async ({ page }) => {
const dropDownMenu = page.locator('body > .tiny-dropdown-menu')
const hoverTrigger = dropDown.first()
const clickTrigger = dropDown.nth(1)
const contextTrigger = dropDown.nth(2)

// hover
await page.waitForTimeout(2000)
Expand All @@ -20,4 +21,16 @@ test('触发方式', async ({ page }) => {
await expect(dropDownMenu.nth(1)).not.toBeVisible()
await clickTrigger.click()
await expect(dropDownMenu.nth(1)).toBeVisible()

// contextmenu
await page.waitForTimeout(2000)
await contextTrigger.hover()
await expect(dropDownMenu.nth(2)).not.toBeVisible()

// 右键触发
await contextTrigger.click({
button: 'right'
})

await expect(dropDownMenu.nth(2)).toBeVisible()
})
13 changes: 13 additions & 0 deletions examples/sites/demos/pc/app/dropdown/trigger.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@
</tiny-dropdown-menu>
</template>
</tiny-dropdown>

<p>场景 3:设置右键触发下拉</p>
<tiny-dropdown trigger="contextmenu">
<template #dropdown>
<tiny-dropdown-menu>
<tiny-dropdown-item :icon="tinyIconPlus">黄金糕</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconPlusCircle">狮子头</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconPlusSquare">螺蛳粉</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconCheckedLinear">双皮奶</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconCheckedSur">蚵仔煎</tiny-dropdown-item>
Comment on lines +33 to +37
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix incorrect variable references.

The :icon bindings reference tinyIconPlus, tinyIconPlusCircle, etc., but these variables don't exist in the data properties (lines 60-66). This will cause runtime errors. Scenario 2 (lines 20-24) correctly uses iconPlus, iconPlusCircle, etc.

Apply this diff to fix the variable references:

-          <tiny-dropdown-item :icon="tinyIconPlus">黄金糕</tiny-dropdown-item>
-          <tiny-dropdown-item :icon="tinyIconPlusCircle">狮子头</tiny-dropdown-item>
-          <tiny-dropdown-item :icon="tinyIconPlusSquare">螺蛳粉</tiny-dropdown-item>
-          <tiny-dropdown-item :icon="tinyIconCheckedLinear">双皮奶</tiny-dropdown-item>
-          <tiny-dropdown-item :icon="tinyIconCheckedSur">蚵仔煎</tiny-dropdown-item>
+          <tiny-dropdown-item :icon="iconPlus">黄金糕</tiny-dropdown-item>
+          <tiny-dropdown-item :icon="iconPlusCircle">狮子头</tiny-dropdown-item>
+          <tiny-dropdown-item :icon="iconPlusSquare">螺蛳粉</tiny-dropdown-item>
+          <tiny-dropdown-item :icon="iconCheckedLinear">双皮奶</tiny-dropdown-item>
+          <tiny-dropdown-item :icon="iconCheckedSur">蚵仔煎</tiny-dropdown-item>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<tiny-dropdown-item :icon="tinyIconPlus">黄金糕</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconPlusCircle">狮子头</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconPlusSquare">螺蛳粉</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconCheckedLinear">双皮奶</tiny-dropdown-item>
<tiny-dropdown-item :icon="tinyIconCheckedSur">蚵仔煎</tiny-dropdown-item>
<tiny-dropdown-item :icon="iconPlus">黄金糕</tiny-dropdown-item>
<tiny-dropdown-item :icon="iconPlusCircle">狮子头</tiny-dropdown-item>
<tiny-dropdown-item :icon="iconPlusSquare">螺蛳粉</tiny-dropdown-item>
<tiny-dropdown-item :icon="iconCheckedLinear">双皮奶</tiny-dropdown-item>
<tiny-dropdown-item :icon="iconCheckedSur">蚵仔煎</tiny-dropdown-item>
🤖 Prompt for AI Agents
In examples/sites/demos/pc/app/dropdown/trigger.vue around lines 33 to 37, the
template uses non-existent variables tinyIconPlus, tinyIconPlusCircle,
tinyIconPlusSquare, tinyIconCheckedLinear, and tinyIconCheckedSur causing
runtime errors; change those :icon bindings to the existing data property names
iconPlus, iconPlusCircle, iconPlusSquare, iconCheckedLinear, and iconCheckedSur
respectively so the template references the defined variables.

</tiny-dropdown-menu>
</template>
</tiny-dropdown>
</div>
</template>

Expand Down
8 changes: 4 additions & 4 deletions examples/sites/demos/pc/app/dropdown/webdoc/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ export default {
},
desc: {
'zh-CN':
'<p>通过 <code>trigger</code> 属性设置触发下拉的方式,默认为 <code>hover</code>。可选值为:<code>click</code> / <code>hover</code> 。</p>\n',
'<p>通过 <code>trigger</code> 属性设置触发下拉的方式,默认为 <code>hover</code>。可选值为:<code>click</code> / <code>hover</code> / <code>contextmenu</code>(3.28.0 起支持)。</p>\n',
'en-US':
'<p>By setting the <code>trigger</code> attribute to trigger a drop-down, the default is <code>hover</code>. The optional values are: <code>click</code> / <code>hover</code>.</p>\n'
'<p>Use the <code>trigger</code> attribute to set how the dropdown is triggered. Default is <code>hover</code>. Optional values: <code>click</code> / <code>hover</code> / <code>contextmenu</code> (supported since 3.28.0).</p>\n'
},
codeFiles: ['trigger.vue']
},
Expand All @@ -108,7 +108,7 @@ export default {
desc: {
'zh-CN': '<p>通过 <code>visible</code> 属性手动控制下拉菜单显隐,优先级高于trigger。</p>\n',
'en-US':
'<p>Manually control the visibility of the dropdown menu through the<code>visible</code>attribute, with priority over trigger.</p>\n'
'<p>Manually control the visibility of the dropdown menu through the <code>visible</code>attribute, with priority over trigger.</p>\n'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add missing space in the English description.

There's a missing space between </code> and attribute in the text.

Apply this diff:

-          '<p>Manually control the visibility of the dropdown menu through the <code>visible</code>attribute, with priority over trigger.</p>\n'
+          '<p>Manually control the visibility of the dropdown menu through the <code>visible</code> attribute, with priority over trigger.</p>\n'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'<p>Manually control the visibility of the dropdown menu through the <code>visible</code>attribute, with priority over trigger.</p>\n'
'<p>Manually control the visibility of the dropdown menu through the <code>visible</code> attribute, with priority over trigger.</p>\n'
🤖 Prompt for AI Agents
In examples/sites/demos/pc/app/dropdown/webdoc/dropdown.js around line 111, the
English description string is missing a space between the closing code tag and
the word "attribute"; update the string to insert a single space after "</code>"
so it reads "</code> attribute" (adjust surrounding escaping/quotes if needed)
to correct the grammar.

},
codeFiles: ['visible.vue']
},
Expand Down Expand Up @@ -307,7 +307,7 @@ export default {
support: {
value: true
},
description: '支持点击和悬停两种触发方式。',
description: '支持点击、悬停以及右键(contextmenu,3.28.0 起支持)触发方式。',
cloud: {
value: true
},
Expand Down
17 changes: 13 additions & 4 deletions packages/renderless/src/dropdown/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const show =
() => {
state.visible = true
},
state.trigger === 'click' ? 0 : props.showTimeout
state.trigger === 'click' || state.trigger === 'contextmenu' ? 0 : props.showTimeout
)
}
}
Expand All @@ -82,7 +82,7 @@ export const hide =
() => {
state.visible = false
},
state.trigger === 'click' ? 0 : props.hideTimeout
state.trigger === 'click' || state.trigger === 'contextmenu' ? 0 : props.hideTimeout
)
}
}
Expand Down Expand Up @@ -127,7 +127,7 @@ export const handleItemKeyDown =
({ api, props, state, emit }: Pick<IDropdownRenderlessParams, 'api' | 'props' | 'state' | 'emit'>) =>
(event: KeyboardEvent) => {
const keyCode = event.keyCode
const target = event.target
const target = event.target as HTMLElement
const currentIndex = state.menuItemsArray.indexOf(target)
const max = state.menuItemsArray.length - 1

Expand Down Expand Up @@ -215,7 +215,15 @@ export const initEvent =
return
}

if (state.trigger === 'hover') {
/** ---------------------------
* 新增:右键触发 contextmenu
* --------------------------- */
if (state.trigger === 'contextmenu') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你好,这个应该属于新特性了,需要在对应的api文件中添加描述和版本信息哈

on(state.triggerElm, 'contextmenu', (e: MouseEvent) => {
e.preventDefault()
api.handleClick()
})
} else if (state.trigger === 'hover') {
Comment on lines +218 to +226
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Memory leak: inline handler can't be removed in cleanup.

The inline arrow function (e: MouseEvent) => { e.preventDefault(); api.handleClick() } is bound to the contextmenu event, but beforeDistory (line 313) attempts to remove it using api.handleClick as the reference. Since off() requires the exact same function reference to remove a listener, the contextmenu handler will never be cleaned up, causing a memory leak.

Recommended solution: Store the handler function to enable proper cleanup:

+export const handleContextMenu =
+  ({ api }: Pick<IDropdownRenderlessParams, 'api'>) =>
+  (e: MouseEvent) => {
+    e.preventDefault()
+    api.handleClick()
+  }

Then in the API object (where handlers are composed), add:

const api = {
  // ... other handlers
  handleContextMenu: handleContextMenu({ api }),
  // ...
}

Update initEvent:

     if (state.trigger === 'contextmenu') {
-      on(state.triggerElm, 'contextmenu', (e: MouseEvent) => {
-        e.preventDefault()
-        api.handleClick()
-      })
+      on(state.triggerElm, 'contextmenu', api.handleContextMenu)
     } else if (state.trigger === 'hover') {

Update beforeDistory:

-      off(state.triggerElm, 'contextmenu', api.handleClick) /** 右键清理 */
+      off(state.triggerElm, 'contextmenu', api.handleContextMenu)
🤖 Prompt for AI Agents
In packages/renderless/src/dropdown/index.ts around lines 218-226 (and refer to
beforeDistory at line 313), the contextmenu listener is added with an inline
arrow function so off() later cannot remove it; replace the inline handler with
a named function stored on the instance/state and expose it via the API (e.g.,
api.handleContextMenu) so you can both add and remove the exact same function
reference. Concretely: create a handler factory or method that accepts the
event, calls preventDefault and api.handleClick, assign it to state or api
(handleContextMenu) during initEvent, use that reference when calling
on(state.triggerElm, 'contextmenu', handler) and remove it in beforeDistory with
off(state.triggerElm, 'contextmenu', handler); also add the new
handleContextMenu entry to the api object where other handlers are composed.

on(state.triggerElm, 'mouseenter', api.show)
on(state.triggerElm, 'mouseleave', api.hide)
on(state.dropdownElm, 'mouseenter', api.show)
Expand Down Expand Up @@ -302,6 +310,7 @@ export const beforeDistory =
off(state.triggerElm, 'mouseenter', api.show)
off(state.triggerElm, 'mouseleave', api.hide)
off(state.triggerElm, 'click', api.handleClick)
off(state.triggerElm, 'contextmenu', api.handleClick) /** 右键清理 */
state.triggerElm = null
}

Expand Down
Loading