123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- import { computed, getCurrentInstance, onMounted, watch } from 'vue'
- import { isFunction } from '@vue/shared'
- import { isClient } from '@vueuse/core'
- import { buildProp, definePropType, isBoolean } from '@element-plus/utils'
- import type { ExtractPropType } from '@element-plus/utils'
- import type { RouteLocationNormalizedLoaded } from 'vue-router'
- import type { ComponentPublicInstance, ExtractPropTypes, Ref } from 'vue'
- const _prop = buildProp({
- type: definePropType<boolean | null>(Boolean),
- default: null,
- } as const)
- const _event = buildProp({
- type: definePropType<(val: boolean) => void>(Function),
- } as const)
- export type UseModelTogglePropsRaw<T extends string> = {
- [K in T]: typeof _prop
- } & {
- [K in `onUpdate:${T}`]: typeof _event
- }
- export type UseModelTogglePropsGeneric<T extends string> = {
- [K in T]: ExtractPropType<typeof _prop>
- } & {
- [K in `onUpdate:${T}`]: ExtractPropType<typeof _event>
- }
- export const createModelToggleComposable = <T extends string>(name: T) => {
- const updateEventKey = `update:${name}` as const
- const updateEventKeyRaw = `onUpdate:${name}` as const
- const useModelToggleEmits = [updateEventKey]
- const useModelToggleProps = {
- [name]: _prop,
- [updateEventKeyRaw]: _event,
- } as UseModelTogglePropsRaw<T>
- const useModelToggle = ({
- indicator,
- toggleReason,
- shouldHideWhenRouteChanges,
- shouldProceed,
- onShow,
- onHide,
- }: ModelToggleParams) => {
- const instance = getCurrentInstance()!
- const { emit } = instance
- const props = instance.props as UseModelTogglePropsGeneric<T> & {
- disabled: boolean
- }
- const hasUpdateHandler = computed(() =>
- isFunction(props[updateEventKeyRaw])
- )
- // when it matches the default value we say this is absent
- // though this could be mistakenly passed from the user but we need to rule out that
- // condition
- const isModelBindingAbsent = computed(() => props[name] === null)
- const doShow = (event?: Event) => {
- if (indicator.value === true) {
- return
- }
- indicator.value = true
- if (toggleReason) {
- toggleReason.value = event
- }
- if (isFunction(onShow)) {
- onShow(event)
- }
- }
- const doHide = (event?: Event) => {
- if (indicator.value === false) {
- return
- }
- indicator.value = false
- if (toggleReason) {
- toggleReason.value = event
- }
- if (isFunction(onHide)) {
- onHide(event)
- }
- }
- const show = (event?: Event) => {
- if (
- props.disabled === true ||
- (isFunction(shouldProceed) && !shouldProceed())
- )
- return
- const shouldEmit = hasUpdateHandler.value && isClient
- if (shouldEmit) {
- emit(updateEventKey, true)
- }
- if (isModelBindingAbsent.value || !shouldEmit) {
- doShow(event)
- }
- }
- const hide = (event?: Event) => {
- if (props.disabled === true || !isClient) return
- const shouldEmit = hasUpdateHandler.value && isClient
- if (shouldEmit) {
- emit(updateEventKey, false)
- }
- if (isModelBindingAbsent.value || !shouldEmit) {
- doHide(event)
- }
- }
- const onChange = (val: boolean) => {
- if (!isBoolean(val)) return
- if (props.disabled && val) {
- if (hasUpdateHandler.value) {
- emit(updateEventKey, false)
- }
- } else if (indicator.value !== val) {
- if (val) {
- doShow()
- } else {
- doHide()
- }
- }
- }
- const toggle = () => {
- if (indicator.value) {
- hide()
- } else {
- show()
- }
- }
- watch(() => props[name], onChange)
- if (
- shouldHideWhenRouteChanges &&
- instance.appContext.config.globalProperties.$route !== undefined
- ) {
- watch(
- () => ({
- ...(
- instance.proxy as ComponentPublicInstance<{
- $route: RouteLocationNormalizedLoaded
- }>
- ).$route,
- }),
- () => {
- if (shouldHideWhenRouteChanges.value && indicator.value) {
- hide()
- }
- }
- )
- }
- onMounted(() => {
- onChange(props[name])
- })
- return {
- hide,
- show,
- toggle,
- hasUpdateHandler,
- }
- }
- return {
- useModelToggle,
- useModelToggleProps,
- useModelToggleEmits,
- }
- }
- const { useModelToggle, useModelToggleProps, useModelToggleEmits } =
- createModelToggleComposable('modelValue')
- export { useModelToggle, useModelToggleEmits, useModelToggleProps }
- export type UseModelToggleProps = ExtractPropTypes<typeof useModelToggleProps>
- export type ModelToggleParams = {
- indicator: Ref<boolean>
- toggleReason?: Ref<Event | undefined>
- shouldHideWhenRouteChanges?: Ref<boolean>
- shouldProceed?: () => boolean
- onShow?: (event?: Event) => void
- onHide?: (event?: Event) => void
- }
|