Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shadow DOM support #753

Open
wants to merge 4 commits into
base: old
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules/
npm-debug.log
coverage/
.idea/
dist/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ Or a reactive property:
- `trigger` - Events triggering the tooltip separated with spaces: `'hover'`, `'click'`, `'focus'` or `'manual'` (`'manual'` can't be combined with any other event).
- `show` - Boolean to manually open or hide the tooltip.
- `offset` - Offset of the position (px).
- `container` - Selector: Container where the tooltip will be appended (e.g. `'body'`). Set it to `false` to append popover on target parent node.
- `container` - Selector: Container where the tooltip will be appended (e.g. `'body'`). Set it to `false` to append popover on target parent node. Provide a callback to resolve container dynamically, signature would be `callback(targetElement)`.
- `boundariesElement` - DOM element for the tooltip boundaries.
- `template` - HTML template of the tooltip.
- `arrowSelector` - CSS selector to get the arrow element in the tooltip template.
Expand Down
6 changes: 6 additions & 0 deletions docs-src/PageHome.vue
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@
</Collapse>
</section>

<!-- <section class="demo">
<div class="section-content"><TooltipShadowDOMContainer/></div>
</section>-->

</div>
</template>

Expand All @@ -314,6 +318,7 @@ import screenfull from 'screenfull'
import CodeSnippet from './CodeSnippet.vue'
import Collapse from './Collapse.vue'
import ExampleComponent from './ExampleComponent.vue'
import TooltipShadowDOMContainer from './shadow/TooltipShadowDOMContainer'

const mainSnippet = `
import Vue from 'vue'
Expand Down Expand Up @@ -612,6 +617,7 @@ export default {
CodeSnippet,
Collapse,
ExampleComponent,
TooltipShadowDOMContainer
},

data () {
Expand Down
38 changes: 38 additions & 0 deletions docs-src/shadow/TooltipShadowDOMContainer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<template>
<div ref="host" class="tooltip-shadow-dom-demo">
<span style="color: red; font-weight: bold;">ShadowDOM Container</span>
</div>
</template>

<script>
import Vue from 'vue'
import TooltipShadowDOMContent from './TooltipShadowDOMContent'

export default {

data () {
return {
shadowRoot: null,
shadowApp: null,
}
},

async mounted() {
const host = this.$refs.host;
this.shadowRoot = host.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = '<style>.tooltip { background-color: lightpink } </style>';
const el = document.createElement('div');
const appEl = document.createElement('div');
el.appendChild(appEl);
this.shadowRoot.appendChild(el);
this.shadowApp = new Vue({
el: appEl,
render: (h) => h(
TooltipShadowDOMContent,
{ props: {} },
)
})
this.shadowApp.$root.shadowRoot = this.shadowRoot;
}
}
</script>
36 changes: 36 additions & 0 deletions docs-src/shadow/TooltipShadowDOMContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<template>
<div class="tooltip-shadow-dom-demo-content">
<h2>ShadowDOM Content</h2>
<input class="tooltip-content" v-model="msg" placeholder="Tooltip content"/>
<button
class="tooltip-target"
title="This is a button"
v-tooltip.bottom="{
content: msg,
container: el
}">Hover me</button>
</div>
</template>

<script>
import Vue from 'vue'

export default {

props: {},

data () {
return {
msg: 'This is a button within shadowDOM',
el: (targetElement) => this.$root.$el.parentElement,
}
},

}
</script>

<style lang="scss">

</style>


2 changes: 1 addition & 1 deletion src/components/Popover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ function handleGlobalClose (event, touch = false) {
for (let i = 0; i < openPopovers.length; i++) {
const popover = openPopovers[i]
if (popover.$refs.popover) {
const contains = popover.$refs.popover.contains(event.target)
const contains = popover.$refs.popover.contains(event.composedPath()[0])
requestAnimationFrame(() => {
if (event.closeAllPopover || (event.closePopover && contains) || (popover.autoHide && !contains)) {
popover.$_handleGlobalClose(event, touch)
Expand Down
13 changes: 7 additions & 6 deletions src/lib/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import Popper from 'popper.js'
import { getOptions, directive } from '../directives/v-tooltip'
import { addClasses, removeClasses, supportsPassive } from '../utils'
import { addClasses, isInDocument, removeClasses, supportsPassive } from '../utils'
import isEqual from 'lodash/isEqual'

const DEFAULT_OPTIONS = {
Expand Down Expand Up @@ -287,8 +287,8 @@ export default class Tooltip {
}

_show (reference, options) {
if (options && typeof options.container === 'string') {
const container = document.querySelector(options.container)
if (options) {
const container = this._findContainer(options.container, reference)
if (!container) return
}

Expand Down Expand Up @@ -490,6 +490,9 @@ export default class Tooltip {
// if container is a query, get the relative element
if (typeof container === 'string') {
container = window.document.querySelector(container)
} else if (typeof container === 'function') {
// if container is function that returns an element, resolve it
container = container(reference)
} else if (container === false) {
// if container is `false`, set it to reference parent
container = reference.parentNode
Expand Down Expand Up @@ -578,9 +581,7 @@ export default class Tooltip {
if (this._isOpen === false) {
return
}
if (!this._tooltipNode.ownerDocument.body.contains(this._tooltipNode)) {
return
}
if (!isInDocument(this._tooltipNode)) return

// if we are hiding because of a mouseleave, we must check that the new
// reference isn't the tooltip, because in this case we don't want to hide it
Expand Down
19 changes: 19 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@ export function convertToArray (value) {
return value
}

/**
* Check if an element is in the DOM (Including the Shadow DOM).
* @param element
* @returns {boolean}
*/
export function isInDocument (element) {
let currentElement = element
while (currentElement && currentElement.parentNode) {
if (currentElement.parentNode === document) {
return true
} else if (currentElement.parentNode instanceof DocumentFragment) {
currentElement = currentElement.parentNode.host
} else {
currentElement = currentElement.parentNode
}
}
return false
}

/**
* Add classes to an element.
* This method checks to ensure that the classes don't already exist before adding them.
Expand Down
5 changes: 2 additions & 3 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ export interface GlobalVTooltipOptions {
* Default container where the tooltip will be appended
* @default 'body'
*/
defaultContainer: string | HTMLElement | false

defaultContainer: string | HTMLElement | false | ((targetElement: Element) => Element)
defaultBoundariesElement: string | HTMLElement

defaultPopperOptions: any
Expand Down Expand Up @@ -203,4 +202,4 @@ export default vToolTip;

export const VPopover: VueConstructor<Vue>;
export const VClosePopover: DirectiveOptions;
export const VTooltip: DirectiveOptions;
export const VTooltip: DirectiveOptions;