I. Component introduction

Website links: the Collapse folding panel | Element (gitee. IO)

Collapse Panel components collapse/expand content areas and are often used in combination with collapsion-item components.

1.1 the collapse property

  • V-model /model-value: string/array type. Specifies the currently active panel. In accordion mode, the value must be of type string.
  • Accordion: Boolean type; Whether accordion mode (only one panel can be expanded at a time), default false;

1.2 the collapse – item properties

  • Name: a unique identifier of string/number. If it is not passed in, a random four-digit number is generated as name.
  • Title: String, panel title;
  • Disabled: Boolean. The default value is false.

1.3 collapse-item named slot

  • Title: You can use the named slot to customize the title

Second, source code analysis

2.1 Collapse component source code

<template>
  <div class="el-collapse" role="tablist" aria-multiselectable="true">
    <slot></slot>
  </div>
</template>

<script lang="ts">
// Mitt is an event bus library that provides on/ EMIT /off functions for event monitoring and emission
import mitt, { Emitter } from 'mitt'
setup(props, { emit }) {
    // modelValue can be a string or an array, and is set to an array type with []. Concat
    const activeNames = ref([].concat(props.modelValue))
    // Generate an event bus
    const collapseMitt: Emitter = mitt()
    // Set the active panel with the name of the panel
    const setActiveNames = _activeNames= > {
      // Convert to an array
      activeNames.value = [].concat(_activeNames)
      // In accordion mode, just take the first element
      const value = props.accordion ? activeNames.value[0] : activeNames.value
      // Launch the V-Model event up
      emit(UPDATE_MODEL_EVENT, value)
      // Fire up the change event
      emit(CHANGE_EVENT, value)
    }
    
    // item click event
    const handleItemClick = name= > {
      if (props.accordion) {
        // Accordion mode
        setActiveNames(
          (activeNames.value[0] || activeNames.value[0= = =0) &&
            activeNames.value[0] === name
            ? ' '
            : name,
        )
      } else {
        const _activeNames = activeNames.value.slice(0)
        const index = _activeNames.indexOf(name)
        // The current item is already expanded, and this click closes the operation
        if (index > -1) {
          _activeNames.splice(index, 1)}else {
        // The current item is not expanded, this click is open operation
          _activeNames.push(name)
        }
        / / call setActiveNames
        setActiveNames(_activeNames)
      }
    }
    
    // Listen for modelValue
    watch(
      () = > props.modelValue,
      () = > {
        activeNames.value = [].concat(props.modelValue)
      },
    )
    
    // The mitt event bus listens for item-click, using handleItemClick as a handler
    collapseMitt.on('item-click', handleItemClick)

    onUnmounted(() = > {
      // Clear all event listeners during uninstallation to prevent memory leaks
      collapseMitt.all.clear()
    })
    
    // Provide data to child components
    provide('collapse', {
      activeNames,
      collapseMitt,
    })

    return {
      activeNames,
      setActiveNames,
      handleItemClick,
    }
  },
})
</script>
Copy the code

Summary: The collapse component code is relatively simple, the Template part provides the default slot, the Script part maintains the activation panel and other data, uses the Mitt event bus to communicate with the sub-components and listens for item-click events.

2.2 the collapse – item component

template

<template>
  <div
    class="el-collapse-item"
    :class="{'is-active': isActive, 'is-disabled': disabled }"
  >// The header section<div
      role="tab"
      :aria-expanded="isActive"
      :aria-controls="`el-collapse-content-${id}`"
      :aria-describedby="`el-collapse-content-${id}`"
    >
      <div
        :id="`el-collapse-head-${id}`"
        class="el-collapse-item__header"
        role="button"
        :tabindex="disabled ? 1:0."
        :class="{ 'focusing': focusing, 'is-active': isActive }"
        @click="handleHeaderClick"
        @keyup.space.enter.stop="handleEnterClick"
        @focus="handleFocus"
        @blur="focusing = false"
      >// slot Named slot<slot name="title">{{ title }}</slot>// Expand/collapse arrows<i
          class="el-collapse-item__arrow el-icon-arrow-right"
          :class="{'is-active': isActive}"
        >
        </i>
      </div>
    </div>// El-collapsor-transition is an animation component that uses the official transition component of VUE and binds the enter/leave hook functions<el-collapse-transition>// Content section<div
        v-show="isActive"
        :id="`el-collapse-content-${id}`"
        class="el-collapse-item__wrap"
        role="tabpanel"
        :aria-hidden=! "" isActive"
        :aria-labelledby="`el-collapse-head-${id}`"
      >
        <div class="el-collapse-item__content">
          <slot></slot>
        </div>
      </div>
    </el-collapse-transition>
  </div>
</template>
Copy the code

Script part

setup(props) {
    // Inject data provided by the parent component
    const collapse = inject<CollapseProvider>('collapse')
    // Use the mitt event bus provided by the parent component
    constcollapseMitt = collapse? .collapseMittconst contentWrapStyle = ref({
      height: 'auto'.display: 'block',})const contentHeight = ref(0)
    const focusing = ref(false)
    const isClick = ref(false)
    // Generate a random Id
    const id = ref(generateId())
    
    // Dynamically calculates whether the parent component's activeNames contains the current name
    const isActive = computed(() = > {
      returncollapse? .activeNames.value.indexOf(props.name) > -1
    })
    
    const handleFocus = () = > {
      setTimeout(() = > {
        if(! isClick.value) { focusing.value =true
        } else {
          isClick.value = false}},50)}// Click the title event
    const handleHeaderClick = () = > {
      if(props.disabled) return
      // Through the mitt event bus, send the item-click event with the name of the current panelcollapseMitt? .emit('item-click', props.name)
      focusing.value = false
      isClick.value = true
    }
    // The Enter key event, which also sends the item-click event
    const handleEnterClick = () = >{ collapseMitt? .emit('item-click', props.name)
    }

    return {
      isActive,
      contentWrapStyle,
      contentHeight,
      focusing,
      isClick,
      id,
      handleFocus,
      handleHeaderClick,
      handleEnterClick,
      collapse,
    }
  },
Copy the code

2.3 summarize

  1. mittIs an event bus library that provideson/emit/off/clearMethods;
  2. Use Transition to display transitions. Transitions can be controlled using JAVASCRIPT or CSS.