feat(hooks): redesign popper apis (#2529)

* feat(hooks): redesign popper apis

- Redesign use-popper APIs to make it more adaptable.

* - Reorganize the popper APIs
This commit is contained in:
jeremywu 2021-07-15 14:08:13 +08:00 committed by GitHub
parent a99b20a8c1
commit c28d7b3738
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 145 deletions

View File

@ -30,7 +30,7 @@ import useTeleport from '../use-teleport'
import useTimeout from '../use-timeout'
import { useModelToggle } from '../use-model-toggle'
import { useTransitionFallthrough } from '../use-transition-fallthrough'
import { usePopperOptions } from './use-popper-options'
import { defaultPopperOptions, defaultModifiers } from './use-popper-options'
import { useTargetEvents, DEFAULT_TRIGGER } from './use-target-events'
import type {
@ -41,9 +41,7 @@ import type {
} from 'vue'
import type {
Instance as PopperInstance,
Options,
Placement,
PositioningStrategy,
StrictModifiers,
} from '@popperjs/core'
import type { RefElement } from '@element-plus/utils/types'
import type { Trigger } from './use-target-events'
@ -56,47 +54,23 @@ type ElementType = ComponentPublicInstance | HTMLElement
export const DARK_EFFECT = 'dark'
export const LIGHT_EFFECT = 'light'
const DEFAULT_FALLBACK_PLACEMENTS = []
export const popperConfigs = {
export const usePopperControlProps = {
appendToBody: {
type: Boolean,
default: true,
},
arrowOffset: {
type: Number,
default: 5,
},
fallbackPlacements: {
type: Array as PropType<Placement[]>,
default: () => DEFAULT_FALLBACK_PLACEMENTS,
},
gpuAcceleration: {
type: Boolean,
default: true,
},
offset: {
type: Number,
default: 12,
},
placement: {
type: String as PropType<Placement>,
default: 'bottom' as Placement,
},
// Once this option were given, the entire popper is under the users' control, top priority
popperOptions: {
type: Object as PropType<Options>,
default: () => null,
},
strategy: {
type: String as PropType<PositioningStrategy>,
default: 'fixed' as PositioningStrategy,
popperOptions: defaultPopperOptions,
popperClass: {
type: String,
default: '',
},
}
export const usePopperProps = {
...popperConfigs,
...usePopperControlProps,
// the arrow size is an equailateral triangle with 10px side length, the 3rd side length ~ 14.1px
// adding a offset to the ceil of 4.1 should be 5 this resolves the problem of arrow overflowing out of popper.
autoClose: {
@ -107,10 +81,7 @@ export const usePopperProps = {
type: String,
default: '',
},
class: {
type: String,
default: '',
},
class: String,
style: Object,
hideAfter: {
type: Number,
@ -136,10 +107,6 @@ export const usePopperProps = {
type: Number,
default: 0,
},
popperClass: {
type: String,
default: '',
},
pure: {
type: Boolean,
default: false,
@ -176,7 +143,6 @@ export const usePopper = () => {
const triggerRef = ref<ElementType>(null)
const popperRef = ref<RefElement>(null)
const popperOptions = usePopperOptions(arrowRef)
const popperStyle = ref<CSSProperties>({ zIndex: PopupManager.nextZIndex() })
const visible = ref(false)
const isManual = computed(() => props.manualMode || props.trigger === 'manual')
@ -274,10 +240,33 @@ export const usePopper = () => {
const $el = isHTMLElement(unwrappedTrigger)
? unwrappedTrigger
: (unwrappedTrigger as ComponentPublicInstance).$el
popperInstance = createPopper($el, popperRef.value, popperOptions.value)
popperInstance = createPopper($el, popperRef.value, buildPopperOptions())
popperInstance.update()
}
function buildPopperOptions() {
const modifiers = [
...defaultModifiers,
...props.popperOptions.modifiers,
]
if (props.showArrow) {
modifiers.push({
name: 'arrow',
options: {
padding: props.arrowOffset || 5,
element: arrowRef.value,
},
} as StrictModifiers)
}
return {
...props.popperOptions,
modifiers,
}
}
const {
onAfterEnter,
onAfterLeave,

View File

@ -1,108 +1,50 @@
import { computed, getCurrentInstance } from 'vue'
import type { Ref } from 'vue'
import type {
Options,
Placement,
StrictModifiers,
PositioningStrategy,
} from '@popperjs/core'
import type { PropType } from 'vue'
import type { Options } from '@popperjs/core'
interface IUsePopperProps {
arrowOffset: number
fallbackPlacements: Array<Placement>
gpuAcceleration: boolean
offset: number
popperOptions: Options
placement: Placement
strategy: PositioningStrategy
}
export const DEFAULT_FALLBACK_PLACEMENTS = []
export const usePopperOptions = (arrowRef: Ref<HTMLElement>) => {
const vm = getCurrentInstance()
export const defaultModifiers = [
{
name: 'offset',
options: {
offset: [0, 12],
},
},
{
name: 'preventOverflow',
options: {
padding: {
top: 2,
bottom: 2,
left: 5,
right: 5,
},
},
},
{
name: 'flip',
options: {
padding: 5,
fallbackPlacements: [],
},
},
{
name: 'computeStyles',
options: {
gpuAcceleration: true,
adaptive: true,
},
},
]
const props = vm.props as unknown as IUsePopperProps
return computed(() => {
export const defaultPopperOptions = {
type: Object as PropType<Options>,
default: () => {
return {
placement: props.placement,
strategy: props.strategy,
...props.popperOptions,
// Avoiding overriding modifiers.
modifiers: buildModifiers({
arrow: arrowRef.value,
arrowOffset: props.arrowOffset,
offset: props.offset,
gpuAcceleration: props.gpuAcceleration,
fallbackPlacements: props.fallbackPlacements,
}, props.popperOptions?.modifiers),
fallbackPlacements: DEFAULT_FALLBACK_PLACEMENTS,
strategy: 'fixed',
modifiers: defaultModifiers,
}
})
}
interface ModifierProps {
offset?: number
arrow?: HTMLElement
arrowOffset?: number
gpuAcceleration?: boolean
fallbackPlacements?: Array<Placement>
}
function buildModifiers(props: ModifierProps, externalModifiers: StrictModifiers[] = []) {
const {
arrow,
arrowOffset,
offset,
gpuAcceleration,
fallbackPlacements,
} = props
const modifiers: Array<StrictModifiers> = [
{
name: 'offset',
options: {
offset: [0, offset ?? 12],
},
},
{
name: 'preventOverflow',
options: {
padding: {
top: 2,
bottom: 2,
left: 5,
right: 5,
},
},
},
{
name: 'flip',
options: {
padding: 5,
fallbackPlacements: fallbackPlacements ?? [],
},
},
{
name: 'computeStyles',
options: {
gpuAcceleration,
adaptive: gpuAcceleration,
},
},
// tippyModifier,
]
if (arrow) {
modifiers.push({
name: 'arrow',
options: {
element: arrow,
padding: arrowOffset ?? 5,
},
})
}
modifiers.push(...(externalModifiers))
return modifiers
},
}