diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 1e78fd7..d81f580 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -72,6 +72,7 @@ function getUtils() {
return [
{ text: 'Flowbite Themable', link: '/components/flowbiteThemable/flowbiteThemable.md' },
{ text: 'Toast Provider', link: '/components/toastProvider/toastProvider.md' },
+ { text: 'PLAYGROUND', link: '/components/PLAYGROUND/PLAYGROUND.md' },
]
}
diff --git a/docs/components/PLAYGROUND/PLAYGROUND.md b/docs/components/PLAYGROUND/PLAYGROUND.md
new file mode 100644
index 0000000..7403bf1
--- /dev/null
+++ b/docs/components/PLAYGROUND/PLAYGROUND.md
@@ -0,0 +1,5 @@
+
+
+
diff --git a/docs/components/PLAYGROUND/examples/SlotListenerExample.vue b/docs/components/PLAYGROUND/examples/SlotListenerExample.vue
new file mode 100644
index 0000000..91969bf
--- /dev/null
+++ b/docs/components/PLAYGROUND/examples/SlotListenerExample.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/docs/components/dropdown/dropdown.md b/docs/components/dropdown/dropdown.md
index a4c2746..4428e45 100644
--- a/docs/components/dropdown/dropdown.md
+++ b/docs/components/dropdown/dropdown.md
@@ -1,6 +1,7 @@
# Dropdown
@@ -90,3 +91,47 @@ import { Dropdown, ListGroup, ListGroupItem } from 'flowbite-vue'
```
+
+## Slot - trigger
+
+```vue
+
+
+
+
+ Click trigger
+
+
+
+
+
+
+ Profile
+
+
+
+
+
+ Settings
+
+
+
+
+
+ Messages
+
+
+
+
+
+ Download
+
+
+
+
+```
+
+
+
diff --git a/docs/components/dropdown/examples/DropdownTriggerExample.vue b/docs/components/dropdown/examples/DropdownTriggerExample.vue
new file mode 100644
index 0000000..99b1243
--- /dev/null
+++ b/docs/components/dropdown/examples/DropdownTriggerExample.vue
@@ -0,0 +1,38 @@
+
+
+
+
+ Click trigger
+
+
+
+
+
+
+ Profile
+
+
+
+
+
+ Settings
+
+
+
+
+
+ Messages
+
+
+
+
+
+ Download
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
index d6d0ba4..7916252 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -343,6 +343,12 @@
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"dev": true
},
+ "@types/lodash": {
+ "version": "4.14.182",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
+ "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
+ "dev": true
+ },
"@types/node": {
"version": "18.6.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.2.tgz",
diff --git a/package.json b/package.json
index 7117d92..c40be83 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
"tailwindcss": "^3"
},
"devDependencies": {
+ "@types/lodash": "^4.14.182",
"@types/node": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.30.0",
"@typescript-eslint/parser": "^5.30.0",
@@ -50,6 +51,7 @@
"eslint-plugin-vue": "^9.1.1",
"flowbite": "^1.4.2",
"jsdom": "^20.0.0",
+ "lodash": "^4.17.21",
"postcss": "^8.4.14",
"postcss-prefix-selector": "^1.16.0",
"prettier": "^2.3.2",
diff --git a/src/components/Dropdown/Dropdown.vue b/src/components/Dropdown/Dropdown.vue
index 15f54ed..cfc3cc5 100644
--- a/src/components/Dropdown/Dropdown.vue
+++ b/src/components/Dropdown/Dropdown.vue
@@ -1,18 +1,26 @@
-
-
-
+
+
+
+
+
+
-
+
+
+
@@ -24,6 +32,7 @@ import type { DropdownPlacement } from './types'
import { useDropdownClasses } from './composables/useDropdownClasses'
import Button from '../Button/Button.vue'
import { onClickOutside } from '@vueuse/core'
+import SlotListener from '@/components/utils/SlotListener/SlotListener.vue'
const visible = ref(false)
@@ -37,7 +46,7 @@ const props = defineProps({
default: 'bottom',
},
text: {
- type: String ,
+ type: String,
default: '',
},
transition: {
@@ -54,7 +63,7 @@ const placementTransitionMap: Record = {
}
const transitionName = computed(() => {
- if(props.transition === null) return placementTransitionMap[props.placement]
+ if (props.transition === null) return placementTransitionMap[props.placement]
return props.transition
})
@@ -68,7 +77,7 @@ const { contentClasses, contentStyles } = useDropdownClasses({
})
onClickOutside(wrapper, () => {
- if(!visible.value) return
+ if (!visible.value) return
visible.value = false
})
diff --git a/src/components/utils/SlotListener/SlotListener.vue b/src/components/utils/SlotListener/SlotListener.vue
new file mode 100644
index 0000000..65e70b9
--- /dev/null
+++ b/src/components/utils/SlotListener/SlotListener.vue
@@ -0,0 +1,132 @@
+
diff --git a/src/components/utils/SlotListener/types.ts b/src/components/utils/SlotListener/types.ts
new file mode 100644
index 0000000..6e69022
--- /dev/null
+++ b/src/components/utils/SlotListener/types.ts
@@ -0,0 +1,10 @@
+export type SlotListenerTrigger = 'click' | 'focus' | 'hover'
+
+
+export type TriggerEventHandlers = {
+ onClick: (e: MouseEvent) => void
+ onMouseenter: (e: MouseEvent) => void
+ onMouseleave: (e: MouseEvent) => void
+ onFocus: (e: FocusEvent) => void
+ onBlur: (e: FocusEvent) => void
+}
diff --git a/src/index.ts b/src/index.ts
index 4b42a66..64d1910 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -31,4 +31,6 @@ export { default as Tooltip } from './components/Tooltip/Tooltip.vue'
export { default as Input } from './components/Input/Input.vue'
+export { default as SlotListener } from './components/utils/SlotListener/SlotListener.vue'
+
export * from './composables'
diff --git a/src/utils/getFirstSlotNode.ts b/src/utils/getFirstSlotNode.ts
new file mode 100644
index 0000000..b8ef760
--- /dev/null
+++ b/src/utils/getFirstSlotNode.ts
@@ -0,0 +1,24 @@
+import type { Slots, VNode } from 'vue'
+import { flatten } from './flatten'
+
+// ref: https://github.com/TuSimple/naive-ui/blob/main/src/popover/src/Popover.tsx
+
+export function getFirstSlotVNode (
+ slots: Slots,
+ slotName = 'default',
+ props: unknown = undefined,
+): VNode | null {
+ const slot = slots[slotName]
+ if (!slot) {
+ console.warn('getFirstSlotVNode', `slot[${slotName}] is empty`)
+ return null
+ }
+ const slotContent = flatten(slot(props))
+ // vue will normalize the slot, so slot must be an array
+ if (slotContent.length === 1) {
+ return slotContent[0]
+ } else {
+ console.warn('getFirstSlotVNode', `slot[${slotName}] should have exactly one child`)
+ return null
+ }
+}