/** * `materialBottomSheet(standard)`: the behaviour of ``, spread into its x-data * alongside `open` (entangled with Livewire, or the surrounding Alpine scope's). * * A downward drag follows the pointer and, released past a quarter of the sheet's height or * flicked down, closes it; otherwise it springs back. The drag starts on the handle, or anywhere * on the sheet while its content is scrolled to the top, so scrolling the content still scrolls. */ const DISMISS_FRACTION = 0.25 const FLICK_PX_PER_MS = 0.5 window.materialBottomSheet = (standard = false) => ({ standard, dragged: 0, close() { this.dragged = 0 this.open = typeof this.open === 'boolean' ? false : null }, dragStart(event) { const sheet = this.$refs.sheet const onHandle = event.target.closest('[data-drag-handle]') const atTop = this.$refs.body.scrollTop <= 0 if (event.button !== 0 || (!onHandle && !atTop) || event.target.closest('input, textarea, select, [contenteditable]')) { return } const startY = event.clientY let lastY = startY let lastAt = performance.now() let velocity = 0 const move = (moveEvent) => { const now = performance.now() velocity = (moveEvent.clientY - lastY) / Math.max(now - lastAt, 1) lastY = moveEvent.clientY lastAt = now this.dragged = Math.max(0, moveEvent.clientY - startY) } const end = () => { window.removeEventListener('pointermove', move) window.removeEventListener('pointerup', end) window.removeEventListener('pointercancel', end) if (this.dragged > sheet.offsetHeight * DISMISS_FRACTION || (this.dragged > 0 && velocity > FLICK_PX_PER_MS)) { this.close() } else { this.dragged = 0 } } window.addEventListener('pointermove', move) window.addEventListener('pointerup', end) window.addEventListener('pointercancel', end) }, })