<commit_before><script>
import Overlay from './Overlay'
import prefix from '../mixins/prefix'
import ui from '../mixins/ui'
import overlay from '../mixins/overlay'
import outside from '../directives/outside'
import { getNodes, isValidNodesResolver } from '../utils/context'
import { isString } from 'lodash'
import config from '../managers/config'
import useConfig from '../mixins/config'
import useControllable from '../mixins/controllable'

const TRIGGER_MAP = {
  hover: 'mouseenter'
}

config.defaults(
  {
    hideDelay: 300
  },
  'tooltip'
)

export default {
  name: 'veui-tooltip',
  directives: { outside },
  components: {
    'veui-overlay': Overlay
  },
  mixins: [
    prefix,
    ui,
    overlay,
    useConfig('config', 'tooltip'),
    useControllable([{ prop: 'open', event: 'toggle' }])
  ],
  props: {
    position: {
      type: String,
      default: 'top'
    },
    target: {
      validator (val) {
        return isValidNodesResolver(val)
      }
    },
    trigger: {
      type: String,
      default: 'hover'
    },
    hideDelay: {
      type: Number
    },
    open: {
      type: Boolean,
      default: false
    },
    interactive: {
      type: Boolean,
      default: true
    },
    autofocus: Boolean,
    aimCenter: Boolean
  },
  computed: {
    realTrigger () {
      if (this.trigger === 'custom') {
        return {}
      }
      let open
      let close
      if (isString(this.trigger)) {
        let trigger = this.trigger.split('-')
        open = trigger[0]
        close = trigger[1] || trigger[0]
      }
      open = TRIGGER_MAP[open] || open
      return {
        open,
        close
      }
    },
    realHideDelay () {
      return this.hideDelay == null
        ? this.config['tooltip.hideDelay']
        : this.hideDelay
    },
    targetNode () {
      return this.getTargetNode(this.target)
    },
    outsideOptions () {
      return {
        handler: this.closeHandler,
        refs: [this.targetNode],
        trigger: this.realTrigger.close,
        delay: this.realHideDelay,
        excludeSelf: !this.interactive
      }
    },
    realAutofocus () {
      return this.interactive ? this.autofocus : false
    },
    defaultOverlayOptions () {
      return {
        position: this.position,
        ...(this.aimCenter
          ? {
            offset: {
              offset:
                  this.position.indexOf('start') !== -1
                    ? '50%'
                    : this.position.indexOf('end') !== -1
                      ? '-50%'
                      : 0
            }
          }
          : {})
      }
    }
  },
  watch: {
    targetNode (val, oldVal) {
      this.removeHandler(oldVal)
      this.bindHandler()
    },
    trigger () {
      this.rebindHandler()
    },
    position (val) {
      this.defaultOverlayOptions.position = val
    }
  },
  mounted () {
    this.bindHandler()
  },
  beforeDestroy () {
    this.removeHandler()
  },
  methods: {
    getTargetNode (target) {
      return getNodes(this.target, this.$vnode.context)[0]
    },
    openHandler () {
      this.commit('open', true)
    },
    closeHandler () {
      this.commit('open', false)
    },
    removeHandler (target) {
      let targetNode = target || this.targetNode<commit_msg>fix: position prop of Tooltip can be `null`<commit_after><script>
import Overlay from './Overlay'
import prefix from '../mixins/prefix'
import ui from '../mixins/ui'
import overlay from '../mixins/overlay'
import outside from '../directives/outside'
import { getNodes, isValidNodesResolver } from '../utils/context'
import { isString } from 'lodash'
import config from '../managers/config'
import useConfig from '../mixins/config'
import useControllable from '../mixins/controllable'

const TRIGGER_MAP = {
  hover: 'mouseenter'
}

config.defaults(
  {
    hideDelay: 300
  },
  'tooltip'
)

export default {
  name: 'veui-tooltip',
  directives: { outside },
  components: {
    'veui-overlay': Overlay
  },
  mixins: [
    prefix,
    ui,
    overlay,
    useConfig('config', 'tooltip'),
    useControllable([{ prop: 'open', event: 'toggle' }])
  ],
  props: {
    position: {
      type: String,
      default: 'top'
    },
    target: {
      validator (val) {
        return isValidNodesResolver(val)
      }
    },
    trigger: {
      type: String,
      default: 'hover'
    },
    hideDelay: {
      type: Number
    },
    open: {
      type: Boolean,
      default: false
    },
    interactive: {
      type: Boolean,
      default: true
    },
    autofocus: Boolean,
    aimCenter: Boolean
  },
  computed: {
    realTrigger () {
      if (this.trigger === 'custom') {
        return {}
      }
      let open
      let close
      if (isString(this.trigger)) {
        let trigger = this.trigger.split('-')
        open = trigger[0]
        close = trigger[1] || trigger[0]
      }
      open = TRIGGER_MAP[open] || open
      return {
        open,
        close
      }
    },
    realHideDelay () {
      return this.hideDelay == null
        ? this.config['tooltip.hideDelay']
        : this.hideDelay
    },
    realPosition () {
      return this.position || 'top'
    },
    targetNode () {
      return this.getTargetNode(this.target)
    },
    outsideOptions () {
      return {
        handler: this.closeHandler,
        refs: [this.targetNode],
        trigger: this.realTrigger.close,
        delay: this.realHideDelay,
        excludeSelf: !this.interactive
      }
    },
    realAutofocus () {
      return this.interactive ? this.autofocus : false
    },
    defaultOverlayOptions () {
      let position = this.realPosition
      return {
        position,
        ...(this.aimCenter
          ? {
            offset: {
              offset:
                  position.indexOf('start') !== -1
                    ? '50%'
                    : position.indexOf('end') !== -1
                      ? '-50%'
                      : 0
            }
          }
          : {})
      }
    }
  },
  watch: {
    targetNode (_, oldVal) {
      this.removeHandler(oldVal)
      this.bindHandler()
    },
    trigger () {
      this.rebindHandler()
    },
    position (val) {
      this.defaultOverlayOptions.position = val
    }
  },
  mounted () {
    this.bindHandler()
  },
  beforeDestroy () {
    this.removeHandler()
  },
  methods: {
    getTargetNode (target) {
      return getNodes(this.target, this.$vnode.context)[0]
    },
    openHandler () {
      this.commit('open', true)
    },
    closeHandler () {
      this.commit('open', false)
    },
    removeHandler (target) {
      let targetNode = target || this.targetNode
{'fraction_non_alphanumeric': 0.1349146396736667, 'fraction_numerical': 0.004230246260764466, 'mean_word_length': 1.8412017167381973, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2353147', 'n_tokens_mistral': 2021, 'n_tokens_neox': 1998, 'n_words': 417}