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

enh: remove wrapping element from detachable components #3206

Closed
wants to merge 3 commits into from
Closed
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
26 changes: 9 additions & 17 deletions src/components/VBottomSheet/VBottomSheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import VDialog from '../VDialog/VDialog'
export default {
name: 'v-bottom-sheet',

functional: true,

props: {
disabled: Boolean,
fullWidth: Boolean,
Expand All @@ -20,29 +22,19 @@ export default {
value: null
},

render (h) {
const activator = h('template', {
slot: 'activator'
}, this.$slots.activator)
render (h, context) {
const slots = context.slots()

const contentClass = [
'v-bottom-sheet',
this.inset ? 'v-bottom-sheet--inset' : ''
].join(' ')
const contentClass = 'v-bottom-sheet' +
(context.props.inset ? ' v-bottom-sheet--inset' : '')

return h(VDialog, {
attrs: {
...this.$props
},
on: {
...this.$listeners
},
...context.data,
props: {
contentClass,
noClickAnimation: true,
transition: 'bottom-sheet-transition',
value: this.value
transition: 'bottom-sheet-transition'
}
}, [activator, this.$slots.default])
}, slots.activator ? [h('template', { slot: 'activator' }, slots.activator), slots.default] : slots.default)
}
}
226 changes: 16 additions & 210 deletions src/components/VDialog/VDialog.js
Original file line number Diff line number Diff line change
@@ -1,226 +1,32 @@
import '../../stylus/components/_dialogs.styl'
import VDialogContent from './VDialogContent'

// Mixins
import Dependent from '../../mixins/dependent'
import Detachable from '../../mixins/detachable'
import Overlayable from '../../mixins/overlayable'
import Returnable from '../../mixins/returnable'
import Stackable from '../../mixins/stackable'
import Toggleable from '../../mixins/toggleable'

// Directives
import ClickOutside from '../../directives/click-outside'

// Helpers
import { getZIndex, convertToUnit } from '../../util/helpers'
let counter = 0

/* @vue/component */
export default {
name: 'v-dialog',

directives: {
ClickOutside
},
functional: true,

mixins: [
Dependent,
Detachable,
Overlayable,
Returnable,
Stackable,
Toggleable
],
$_wrapperFor: VDialogContent,

props: {
disabled: Boolean,
persistent: Boolean,
fullscreen: Boolean,
fullWidth: Boolean,
noClickAnimation: Boolean,
maxWidth: {
type: [String, Number],
default: 'none'
},
origin: {
type: String,
default: 'center center'
},
width: {
type: [String, Number],
default: 'auto'
},
scrollable: Boolean,
transition: {
type: [String, Boolean],
default: 'dialog-transition'
}
},
render (h, context) {
const slots = context.slots()
const activator = slots.activator && slots.activator[0]

data () {
return {
animate: false,
animateTimeout: null,
isDependent: false,
stackClass: 'v-dialog__content--active',
stackMinZIndex: 200
}
},
context.data.ref = context.data.ref || '$_v-dialog-' + counter++

computed: {
classes () {
return {
[(`v-dialog ${this.contentClass}`).trim()]: true,
'v-dialog--active': this.isActive,
'v-dialog--persistent': this.persistent,
'v-dialog--fullscreen': this.fullscreen,
'v-dialog--scrollable': this.scrollable,
'v-dialog--animated': this.animate
}
},
contentClasses () {
return {
'v-dialog__content': true,
'v-dialog__content--active': this.isActive
}
}
},
const content = h(VDialogContent, context.data, slots.default)

watch: {
isActive (val) {
if (val) {
this.show()
} else {
this.removeOverlay()
this.unbind()
if (activator) {
activator.data.on = activator.data.on || {}
activator.data.on.click = e => {
e.preventDefault()
const dialog = context.parent.$refs[context.data.ref]
if (!dialog.disabled) dialog.isActive = true
}
}
},

mounted () {
this.isBooted = this.isActive
this.isActive && this.show()
},

beforeDestroy () {
if (typeof window !== 'undefined') this.unbind()
},

methods: {
animateClick () {
this.animate = false
// Needed for when clicking very fast
// outside of the dialog
this.$nextTick(() => {
this.animate = true
clearTimeout(this.animateTimeout)
this.animateTimeout = setTimeout(() => (this.animate = false), 150)
})
},
closeConditional (e) {
// If the dialog content contains
// the click event, or if the
// dialog is not active
if (this.$refs.content.contains(e.target) ||
!this.isActive
) return false

// If we made it here, the click is outside
// and is active. If persistent, and the
// click is on the overlay, animate
if (this.persistent) {
if (!this.noClickAnimation &&
this.overlay === e.target
) this.animateClick()

return false
}

// close dialog if !persistent, clicked outside and we're the topmost dialog.
// Since this should only be called in a capture event (bottom up), we shouldn't need to stop propagation
return getZIndex(this.$refs.content) >= this.getMaxZIndex()
},
show () {
!this.fullscreen && !this.hideOverlay && this.genOverlay()
this.fullscreen && this.hideScroll()
this.$refs.content.focus()
this.$listeners.keydown && this.bind()
},
bind () {
window.addEventListener('keydown', this.onKeydown)
},
unbind () {
window.removeEventListener('keydown', this.onKeydown)
},
onKeydown (e) {
this.$emit('keydown', e)
}
},

render (h) {
const children = []
const data = {
'class': this.classes,
ref: 'dialog',
directives: [
{
name: 'click-outside',
value: () => (this.isActive = false),
args: {
closeConditional: this.closeConditional,
include: this.getOpenDependentElements
}
},
{ name: 'show', value: this.isActive }
],
on: {
click: e => { e.stopPropagation() }
}
}

if (!this.fullscreen) {
data.style = {
maxWidth: this.maxWidth === 'none' ? undefined : convertToUnit(this.maxWidth),
width: this.width === 'auto' ? undefined : convertToUnit(this.width)
}
}

if (this.$slots.activator) {
children.push(h('div', {
'class': 'v-dialog__activator',
on: {
click: e => {
e.stopPropagation()
if (!this.disabled) this.isActive = !this.isActive
}
}
}, [this.$slots.activator]))
}

let dialog = h('div', data, this.showLazyContent(this.$slots.default))
if (this.transition) {
dialog = h('transition', {
props: {
name: this.transition,
origin: this.origin
}
}, [dialog])
}

children.push(h('div', {
'class': this.contentClasses,
attrs: {
tabIndex: '-1',
...this.getScopeIdAttrs()
},
style: { zIndex: this.activeZIndex },
ref: 'content'
}, [dialog]))

return h('div', {
staticClass: 'v-dialog__container',
style: {
display: (!this.$slots.activator || this.fullWidth) ? 'block' : 'inline-block'
}
}, children)
return activator ? [activator, content] : content
}
}
Loading