# Interaction Outside

@vue-cdk/interaction-outside makes it easy to detect user–interactions that happen outside of a specific element/component.

# Installation

The @vue-cdk/interaction-outside package can be installed via NPM/Yarn:

$ npm install @vue-cdk/interaction-outside --save

# Usage

@vue-cdk/interaction-outside exposes a Vue plugin that you can use like this:

import InteractionOutside from '@vue-cdk/interaction-outside'
import Vue from 'vue'

Vue.use(InteractionOutside)

The plugin makes a component called CInteractionOutside globally available.

Just wrap any element or component with CInteractionOutside. CInteractionOutside will listen for events and once user–interaction outside of the wrapped element/component is detected CInteractionOutside emits the detected–event.

# Examples

# Hello World

This is the inside. Click here.
Show Code
<template>
  <div class="outside" :class="outsideClasses">
    {{ outsideText }}
    <CInteractionOutside @detected="outside = true">
      <div class="inside" :class="insideClasses" @click="outside = false">
        {{ innerText }}
      </div>
    </CInteractionOutside>
  </div>
</template>
<script>
export default {
  data() {
    return {
      outside: null,
    }
  },
  computed: {
    innerText() {
      const { outside } = this
      switch (outside) {
        case null: {
          return 'This is the inside. Click here.'
        }
        case false: {
          return 'Now click anywhere – just not here.'
        }
        case true: {
          return 'Click outside of this element detected.'
        }
        default: {
          throw Error('invalid state')
        }
      }
    },
    outsideText() {
      return this.outside === false ? 'You can click here or anywhere else.' : ''
    },
    insideClasses() {
      return {
        'is-active': this.outside === false,
      }
    },
    outsideClasses() {
      return {
        'is-active': this.outside === true,
      }
    },
  },
}
</script>

<style scoped>
.inside {
  text-align: center;
  border: 1px solid #cccccc;
  margin: 1rem;
  padding: 1rem;
  background-color: white;
}
.inside.is-active {
  background-color: rgb(210, 253, 255);
}
.outside {
  text-align: center;
  background-color: white;
  border: 1px solid #cccccc;
  margin: 1rem;
  padding: 1rem;
}
.outside.is-active {
  background-color: rgb(210, 253, 255);
}
</style>

# Ignoring Elements

You can disable the detection of outside interaction for specific elements. Simply set ignored to a method that returns an array of HTMLElements that should be ignored.

This is the inside. Click here.
Show Code
<template>
  <div class="outside" :class="outsideClasses">
    <CInteractionOutside :ignored="ignored" @detected="outside = true">
      <div class="inside" :class="insideClasses" @click="outside = false">
        {{ innerText }}
      </div>
    </CInteractionOutside>
    <div v-show="outside === false" ref="ignored" class="ignored">
      Clicks here are ignored. Try it!
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      outside: null,
    }
  },
  computed: {
    innerText() {
      const { outside } = this
      switch (outside) {
        case null: {
          return 'This is the inside. Click here.'
        }
        case false: {
          return 'Now click anywhere – just not here.'
        }
        case true: {
          return 'Click outside of this element detected.'
        }
        default: {
          throw Error('invalid state')
        }
      }
    },
    outsideText() {
      return this.outside === false ? 'You can click here or anywhere else.' : ''
    },
    insideClasses() {
      return {
        'is-active': this.outside === false,
      }
    },
    outsideClasses() {
      return {
        'is-active': this.outside === true,
      }
    },
  },
  methods: {
    ignored() {
      return [this.$refs.ignored]
    },
  },
}
</script>

<style scoped>
.inside {
  text-align: center;
  border: 1px solid #cccccc;
  margin: 1rem;
  padding: 1rem;
  background-color: white;
}
.ignored {
  text-align: center;
  border: 1px solid #cccccc;
  margin: 1rem;
  padding: 1rem;
  color: rgb(173, 2, 2);
  font-weight: bold;
  background-color: white;
}
.inside.is-active {
  background-color: rgb(210, 253, 255);
}
.outside {
  text-align: center;
  background-color: white;
  border: 1px solid #cccccc;
  margin: 1rem;
  padding: 1rem;
}
.outside.is-active {
  background-color: rgb(210, 253, 255);
}
</style>

# Activating and Deactivating

By default CInteractionOutside is active. An active CInteractionOutside will listen for events and try to detect interaction outside of it. You can disable the detection of outside–interaction by setting active to false. This is useful if you want to temporary disable any accidental actions.

This is the inside. Click here.
Show Code
<template>
  <div class="outside" :class="outsideClasses">
    {{ outsideText }}
    <button @click="active = !active">
      {{ active ? 'Deactivate' : 'Activate' }} CInteractionOutside
    </button>
    <CInteractionOutside :active="active" @detected="outside = true">
      <div class="inside" :class="insideClasses" @click="outside = false">
        {{ innerText }}
      </div>
    </CInteractionOutside>
  </div>
</template>
<script>
export default {
  data() {
    return {
      outside: null,
      active: false,
    }
  },
  computed: {
    innerText() {
      const { outside } = this
      switch (outside) {
        case null: {
          return 'This is the inside. Click here.'
        }
        case false: {
          return 'Now click anywhere – just not here.'
        }
        case true: {
          return 'Click outside of this element detected.'
        }
        default: {
          throw Error('invalid state')
        }
      }
    },
    outsideText() {
      return this.outside === false ? 'You can click here or anywhere else.' : ''
    },
    insideClasses() {
      return {
        'is-active': this.outside === false,
      }
    },
    outsideClasses() {
      return {
        'is-active': this.outside === true,
      }
    },
  },
}
</script>

<style scoped>
.inside {
  text-align: center;
  border: 1px solid #cccccc;
  margin: 1rem;
  padding: 1rem;
  background-color: white;
}
.inside.is-active {
  background-color: rgb(210, 253, 255);
}
.outside {
  text-align: center;
  background-color: white;
  border: 1px solid #cccccc;
  margin: 1rem;
  padding: 1rem;
}
.outside.is-active {
  background-color: rgb(210, 253, 255);
}
</style>

Playground