forked from jd-solanki/anu
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
93372da
commit 5eba736
Showing
6 changed files
with
231 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
<script lang="ts" setup> | ||
import type { Middleware } from '@floating-ui/vue' | ||
import { autoUpdate, flip, shift, useFloating } from '@floating-ui/vue' | ||
import { onClickOutside, useEventListener, useMounted } from '@vueuse/core' | ||
import { ref } from 'vue' | ||
import { sameWidth as sameWidthMiddleware } from './middlewares' | ||
import { floatingProps } from './props' | ||
import { useTeleport } from '@/composables/useTeleport' | ||
import { useInternalBooleanState } from '@/composables/useInternalBooleanState' | ||
const props = defineProps(floatingProps) | ||
const emit = defineEmits<{ | ||
(e: 'update:modelValue', value: boolean): void | ||
}>() | ||
defineOptions({ | ||
name: 'AFloating', | ||
}) | ||
const { teleportTarget } = useTeleport() | ||
const isMounted = useMounted() | ||
const { internalState: isFloatingElVisible, toggle: toggleFloatingElVisibility } = useInternalBooleanState(toRef(props, 'modelValue'), emit, 'update:modelValue', false) | ||
// Template refs | ||
// const props.referenceEl = ref() | ||
const refFloating = ref() | ||
/* | ||
ℹ️ We need to construct the internal middleware variable | ||
If user don't pass the middleware prop then prop value will be `undefined` which will easy to tackle with simple if condition as shown below | ||
Here, we will use user's middleware if passed via props or we will use our defaults | ||
*/ | ||
const _middleware = props.middleware === undefined | ||
? [ | ||
// ℹ️ For this we need need bridge to handle keep menu content open | ||
// offset(6), | ||
sameWidthMiddleware(refFloating), | ||
flip(), | ||
shift({ padding: 10 }), | ||
] as Middleware[] | ||
: props.middleware(props.referenceEl, refFloating) | ||
// Calculate position of floating element | ||
const { x, y, strategy } = useFloating(toRef(props, 'referenceEl'), refFloating, { | ||
strategy: toRef(props, 'strategy'), | ||
placement: toRef(props, 'placement'), | ||
middleware: _middleware, | ||
whileElementsMounted: autoUpdate, | ||
}) | ||
// onMounted(() => { | ||
// const vm = getCurrentInstance() | ||
// console.log('vm?.proxy?.$el :>> ', vm?.proxy?.$parent) | ||
// console.log('vm?.proxy?.$parent.$el :>> ', vm?.proxy?.$parent.$el) | ||
// if (vm?.proxy?.$parent) | ||
// props.referenceEl.value = vm?.proxy?.$parent.$el | ||
// }) | ||
// 👉 Event listeners | ||
/* | ||
If moduleValue is provided don't attach any event to modify the visibility of menu | ||
props.modelValue === undefined => modelValue isn't provided | ||
*/ | ||
if (props.modelValue === undefined) { | ||
// If trigger is hover | ||
if (props.trigger === 'hover') { | ||
// Reference | ||
useEventListener(toRef(props, 'referenceEl'), 'mouseenter', () => { | ||
if (isFloatingElVisible.value === false) | ||
toggleFloatingElVisibility() | ||
}) | ||
useEventListener(toRef(props, 'referenceEl'), 'mouseleave', () => { | ||
if (isFloatingElVisible.value === true) | ||
toggleFloatingElVisibility() | ||
}) | ||
// Floating | ||
useEventListener(refFloating, 'mouseenter', () => { | ||
if (isFloatingElVisible.value === false) | ||
toggleFloatingElVisibility() | ||
}) | ||
useEventListener(refFloating, 'mouseleave', () => { | ||
if (isFloatingElVisible.value === true) | ||
toggleFloatingElVisibility() | ||
}) | ||
} | ||
else { | ||
useEventListener(toRef(props, 'referenceEl'), 'click', toggleFloatingElVisibility) | ||
if (props.persist !== true) { | ||
onClickOutside( | ||
toRef(props, 'referenceEl'), | ||
_event => { | ||
if (isFloatingElVisible.value) | ||
toggleFloatingElVisibility() | ||
}, | ||
{ | ||
ignore: props.persist === 'content' ? [refFloating] : [], | ||
}, | ||
) | ||
} | ||
} | ||
} | ||
</script> | ||
|
||
<template> | ||
<Teleport | ||
v-if="isMounted" | ||
:to="teleportTarget" | ||
> | ||
<!-- ℹ️ Transition component don't accept null as value of name prop so we need `props.transition || undefined` --> | ||
<Transition :name="props.transition || undefined"> | ||
<div | ||
v-show="props.modelValue ?? isFloatingElVisible" | ||
ref="refFloating" | ||
class="a-floating" | ||
:style="{ | ||
top: `${y ?? 0}px`, | ||
left: `${x ?? 0}px`, | ||
}" | ||
:class="strategy" | ||
> | ||
<slot /> | ||
</div> | ||
</Transition> | ||
</Teleport> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as AFloating } from './AFloating.vue' | ||
export * from './props' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import type { ElementRects } from '@floating-ui/vue' | ||
import type { Ref } from 'vue' | ||
|
||
export const sameWidth = (floatingEl: Ref<HTMLElement>) => { | ||
return { | ||
name: 'sameWidth', | ||
fn: ({ rects, x, y }: { rects: ElementRects; x: number; y: number }) => { | ||
// Set width of reference to floating | ||
floatingEl.value.style.minWidth = `${rects.reference.width}px` | ||
|
||
return { x, y } | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import type { Middleware, Placement, Strategy } from '@floating-ui/vue' | ||
import type { ExtractPropTypes, PropType, Ref } from 'vue' | ||
|
||
export const floatingProps = { | ||
referenceEl: { | ||
type: Object as PropType<HTMLElement | any>, | ||
}, | ||
|
||
/** | ||
* Show/Hide floating element base on v-model value | ||
*/ | ||
modelValue: { | ||
type: Boolean, | ||
default: undefined, | ||
}, | ||
|
||
/** | ||
* Persistence of floating element when clicked outside of reference element | ||
*/ | ||
persist: { | ||
type: [Boolean, String] as PropType<boolean | 'content'>, | ||
default: false, | ||
}, | ||
|
||
/** | ||
* Trigger event to open the floating element | ||
*/ | ||
trigger: { | ||
type: String as PropType<'click' | 'hover'>, | ||
default: 'click', | ||
}, | ||
|
||
/** | ||
* Transition to add while showing/hiding floating element | ||
*/ | ||
transition: { | ||
type: [String, null] as PropType<string | null>, | ||
default: 'slide-up', | ||
}, | ||
|
||
// -- Floating UI based Props | ||
|
||
// https://floating-ui.com/docs/computePosition#placement | ||
/** | ||
* Placement option from Floating UI | ||
*/ | ||
placement: { | ||
type: String as PropType<Placement>, | ||
default: 'bottom-start', | ||
}, | ||
|
||
// https://floating-ui.com/docs/computePosition#strategy | ||
/** | ||
* Strategy option from Floating UI | ||
*/ | ||
strategy: { | ||
type: String as PropType<Strategy>, | ||
default: 'absolute', | ||
}, | ||
|
||
// https://floating-ui.com/docs/tutorial#middleware | ||
/** | ||
* Middleware option from Floating UI | ||
*/ | ||
middleware: Function as PropType<(referenceEl: Ref<HTMLElement>, floatingEl: Ref<HTMLElement>) => Middleware[]>, | ||
} | ||
|
||
export type FloatingProps = ExtractPropTypes<typeof floatingProps> |
Oops, something went wrong.