Skip to content

Commit

Permalink
refactor(menu): use floating ui vue package
Browse files Browse the repository at this point in the history
  • Loading branch information
jd-solanki committed Feb 9, 2023
1 parent c052515 commit 93372da
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 87 deletions.
1 change: 1 addition & 0 deletions packages/anu-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
},
"dependencies": {
"@floating-ui/dom": "^1.1.1",
"@floating-ui/vue": "^0.2.0",
"colord": "^2.9.3",
"defu": "^6.1.2",
"vue": "^3.2.47"
Expand Down
78 changes: 30 additions & 48 deletions packages/anu-vue/src/components/menu/AMenu.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script lang="ts" setup>
import type { Middleware, Placement, Strategy } from '@floating-ui/dom'
import { autoUpdate, computePosition, flip, shift } from '@floating-ui/dom'
import type { Middleware, Placement, Strategy } from '@floating-ui/vue'
import { autoUpdate, flip, shift, useFloating } from '@floating-ui/vue'
import { onClickOutside, useEventListener, useMounted } from '@vueuse/core'
import type { PropType } from 'vue'
import { getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import type { PropType, Ref } from 'vue'
import { getCurrentInstance, onMounted, ref } from 'vue'
import { sameWidth as sameWidthMiddleware } from './middlewares'
import { useTeleport } from '@/composables/useTeleport'
import { useInternalBooleanState } from '@/composables/useInternalBooleanState'
Expand Down Expand Up @@ -66,10 +66,7 @@ const props = defineProps({
/**
* Middleware option from Floating UI
*/
middleware: {
type: [Function, Object] as PropType<((referenceEl: HTMLElement, floatingEl: HTMLElement) => Middleware[]) | null>,
default: null,
},
middleware: Function as PropType<(referenceEl: Ref<HTMLElement>, floatingEl: Ref<HTMLElement>) => Middleware[]>,
})
const emit = defineEmits<{
Expand All @@ -88,57 +85,38 @@ const { internalState: isAlertVisible, toggle: toggleAlertVisibility } = useInte
const refReference = ref()
const refFloating = ref()
const calculateFloatingPosition = async () => {
/*
/*
ℹ️ We need to construct the internal middleware variable
If user don't pass the middleware prop then prop value will be `null` which will easy to tackle with simple if condition as shown below
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 === null
? [
// ℹ️ For this we need need bridge to handle keep menu content open
// offset(6),
sameWidthMiddleware(refFloating.value),
flip(),
shift({ padding: 10 }),
] as Middleware[]
: props.middleware(refReference.value, refFloating.value)
// Calculate position of floating element
const { x, y } = await computePosition(refReference.value, refFloating.value, {
strategy: props.strategy,
placement: props.placement,
middleware: _middleware,
})
Object.assign(refFloating.value.style, {
left: `${x}px`,
top: `${y}px`,
})
}
let floatingUiCleanup: Function
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(refReference, refFloating)
// Calculate position of floating element
const { x, y, strategy } = useFloating(refReference, refFloating, {
strategy: toRef(props, 'strategy'),
placement: toRef(props, 'placement'),
middleware: _middleware,
whileElementsMounted: autoUpdate,
})
onMounted(() => {
const vm = getCurrentInstance()
if (vm?.proxy?.$parent)
refReference.value = vm?.proxy?.$parent.$el
})
// Recalculate position if placement changes at runtime
watch(
[isMounted, () => props.placement],
() => {
nextTick(() => {
floatingUiCleanup = autoUpdate(refReference.value, refFloating.value, calculateFloatingPosition)
})
},
)
onBeforeUnmount(() => floatingUiCleanup())
// 👉 Event listeners
/*
If moduleValue is provided don't attach any event to modify the visibility of menu
Expand Down Expand Up @@ -197,7 +175,11 @@ if (props.modelValue === undefined) {
v-show="props.modelValue ?? isAlertVisible"
ref="refFloating"
class="a-menu"
:class="[props.strategy === 'fixed' ? 'fixed' : 'absolute']"
:style="{
top: `${y ?? 0}px`,
left: `${x ?? 0}px`,
}"
:class="strategy"
>
<ACard>
<slot />
Expand Down
5 changes: 3 additions & 2 deletions packages/anu-vue/src/components/menu/middlewares.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { ElementRects } from '@floating-ui/dom'
import type { Ref } from 'vue'

export const sameWidth = (floatingEl: HTMLElement) => {
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.style.minWidth = `${rects.reference.width}px`
floatingEl.value.style.minWidth = `${rects.reference.width}px`

return { x, y }
},
Expand Down
Loading

0 comments on commit 93372da

Please sign in to comment.