svelte-drawer
Version:
A drawer (a.k.a side menu) component.
141 lines (119 loc) • 3.19 kB
HTML
<div
class="
svelte-drawer
side-{{ side }}
axis-{{ axis }}
"
data-open="{{ open }}"
data-opening="{{ opening }}"
data-closing="{{ closing }}"
data-closed="{{ closed }}"
style="
transform: translate3d({{ translate.x }}%, {{ translate.y }}%, 0);
z-index: {{ zIndexBase + 1 }};
"
>
<slot></slot>
</div>
{{#if scrim && !closed && !closing}}
<div
style="z-index: {{ zIndexBase }};"
on:click="close()"
>
<slot name="scrim">
<Scrim/>
</slot>
</div>
{{/if}}
<script>
import { spring } from 'svelte-extras'
import Scrim from 'svelte-scrim'
import clamp from 'just-clamp'
const isLeadingSide = side => side === 'left' || side === 'top'
const DEFAULTS = {
percentOpen: 0,
side: 'left',
scrim: true,
zIndexBase: 1
//push: false // false, element/ref // TODO: push other content over when opening
}
const FIRES = {
open: 'open',
opening: 'opening',
closing: 'closing',
closed: 'closed'
}
;[ DEFAULTS, FIRES ].forEach(Object.freeze)
export default {
setup (Drawer) {
Object.assign(Drawer, { DEFAULTS, FIRES })
},
components: { Scrim },
data () {
return Object.assign({}, DEFAULTS)
},
computed: {
open: percentOpen => percentOpen === 100,
closed: percentOpen => percentOpen < 1,
transitioning: (open, closed) => !open && !closed,
opening: (transitioning, percentOpen, previousPercentOpen) =>
transitioning && (percentOpen > previousPercentOpen),
closing: (transitioning, closed, percentOpen, previousPercentOpen) =>
transitioning && (percentOpen < previousPercentOpen),
axis: side => side === 'left' || side === 'right' ? 'x' : 'y',
translate: (percentOpen, side, axis) => {
const amount = isLeadingSide(side) ? percentOpen - 100 : 100 - percentOpen
return axis === 'x' ? { x: amount, y: 0 } : { x: 0, y: amount }
}
},
oncreate () {
this.observe('percentOpen', (percentOpen, previousPercentOpen) => {
this.set({ previousPercentOpen })
})
// watch data and fire corresponding events
Object.keys(FIRES)
.forEach(property => this.observe(property, v => v && this.fire(property)))
},
methods: {
spring,
setPercentOpen (percent, { smooth = true } = {}) {
const percentOpen = clamp(0, percent, 100)
return smooth
? this.spring('percentOpen', percentOpen, { stiffness: 0.2, damping: 0.9 })
: Promise.resolve(this.set({ percentOpen }))
},
open ({ smooth = true } = {}) {
return this.setPercentOpen(100, { smooth })
},
close ({ smooth = true } = {}) {
return this.setPercentOpen(0, { smooth })
},
toggle ({ smooth = true } = {}) {
const shouldClose = this.get('open') || this.get('opening')
const method = shouldClose ? 'close' : 'open'
return this[method]({ smooth })
}
}
}
</script>
<style>
.svelte-drawer {
position: fixed;
display: inline;
transform: translate3d(0, 0, 0);
}
.axis-x {
top: 0;
bottom: 0;
height: 100%;
}
.axis-y {
left: 0;
right: 0;
width: 100%;
}
.side-top { top: 0 }
.side-right { right: 0 }
.side-bottom { bottom: 0 }
.side-left { left: 0 }
</style>