Created
June 23, 2020 10:28
-
-
Save bnjamin/8a3c530f4861a6b153579eb2fecf18d8 to your computer and use it in GitHub Desktop.
Transitions stolen from alpine.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function transitionIn (el, show = () => {}) { | |
const attrs = getXAttrs(el, 'transition') | |
// If any transition attrs. | |
if (attrs.filter(attr => ['enter', 'enter-start', 'enter-end'].includes(attr.value)).length > 0) { | |
transitionClassesIn(el, attrs, show) | |
} else { | |
// If not, just show that damn thing. | |
show() | |
} | |
} | |
export function transitionOut (el, hide = () => {}) { | |
const attrs = getXAttrs(el, 'transition') | |
if (attrs.filter(attr => ['leave', 'leave-start', 'leave-end'].includes(attr.value)).length > 0) { | |
transitionClassesOut(el, attrs, hide) | |
} else { | |
hide() | |
} | |
} | |
export function transitionClassesIn (el, directives, showCallback) { | |
const enter = (directives.find(i => i.value === 'enter') || { expression: '' }).expression.split(' ').filter(i => i !== '') | |
const enterStart = (directives.find(i => i.value === 'enter-start') || { expression: '' }).expression.split(' ').filter(i => i !== '') | |
const enterEnd = (directives.find(i => i.value === 'enter-end') || { expression: '' }).expression.split(' ').filter(i => i !== '') | |
transitionClasses(el, enter, enterStart, enterEnd, showCallback, () => {}) | |
} | |
export function transitionClassesOut (el, directives, hideCallback) { | |
const leave = (directives.find(i => i.value === 'leave') || { expression: '' }).expression.split(' ').filter(i => i !== '') | |
const leaveStart = (directives.find(i => i.value === 'leave-start') || { expression: '' }).expression.split(' ').filter(i => i !== '') | |
const leaveEnd = (directives.find(i => i.value === 'leave-end') || { expression: '' }).expression.split(' ').filter(i => i !== '') | |
transitionClasses(el, leave, leaveStart, leaveEnd, () => {}, hideCallback) | |
} | |
export function transitionClasses (el, classesDuring, classesStart, classesEnd, hook1, hook2) { | |
const originalClasses = el.__x_original_classes || [] | |
const stages = { | |
start () { | |
el.classList.add(...classesStart) | |
}, | |
during () { | |
el.classList.add(...classesDuring) | |
}, | |
show () { | |
hook1() | |
}, | |
end () { | |
// Don't remove classes that were in the original class attribute. | |
el.classList.remove(...classesStart.filter(i => !originalClasses.includes(i))) | |
el.classList.add(...classesEnd) | |
}, | |
hide () { | |
hook2() | |
}, | |
cleanup () { | |
el.classList.remove(...classesDuring.filter(i => !originalClasses.includes(i))) | |
el.classList.remove(...classesEnd.filter(i => !originalClasses.includes(i))) | |
} | |
} | |
transition(el, stages) | |
} | |
export function transition (el, stages) { | |
stages.start() | |
// Force compute the styles. This is necessary for newly inserted elements in the DOM. | |
// Se this bug report https://bugzilla.mozilla.org/show_bug.cgi?id=916620 | |
const randomProperty = 'width' | |
getComputedStyle(el).getPropertyValue(randomProperty) | |
stages.during() | |
requestAnimationFrame(() => { | |
// Note: Safari's transitionDuration property will list out comma separated transition durations | |
// for every single transition property. Let's grab the first one and call it a day. | |
const duration = Number(getComputedStyle(el).transitionDuration.replace(/,.*/, '').replace('s', '')) * 1000 | |
stages.show() | |
requestAnimationFrame(() => { | |
stages.end() | |
setTimeout(() => { | |
stages.hide() | |
// Adding an "isConnected" check, in case the callback | |
// removed the element from the DOM. | |
if (el.isConnected) { | |
stages.cleanup() | |
} | |
}, duration) | |
}) | |
}) | |
} | |
const xAttrRE = /^x-transition\b/ | |
export function isXAttr (attr) { | |
const name = attr.name | |
return xAttrRE.test(name) | |
} | |
export function getXAttrs (el, type) { | |
return Array.from(el.attributes) | |
.filter(isXAttr) | |
.map(attr => { | |
const name = attr.name | |
const typeMatch = name.match(xAttrRE) | |
const valueMatch = name.match(/:([a-zA-Z\-:]+)/) | |
const modifiers = name.match(/\.[^.\]]+(?=[^\]]*$)/g) || [] | |
return { | |
type: typeMatch ? 'transition' : null, | |
value: valueMatch ? valueMatch[1] : null, | |
modifiers: modifiers.map(i => i.replace('.', '')), | |
expression: attr.value | |
} | |
}) | |
.filter(i => { | |
// If no type is passed in for filtering, bypass filter | |
if (!type) return true | |
return i.type === type | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div data-controller="toggle"> | |
<button data-action="click->toggle#toggle">Toggle</button> | |
<div | |
class="hidden" | |
data-target="toggle.toggleArea" | |
x-transition:enter="transition ease-out duration-100" | |
x-transition:enter-start="transform opacity-0 scale-95" | |
x-transition:enter-end="transform opacity-100 scale-100" | |
x-transition:leave="transition ease-in duration-75" | |
x-transition:leave-star="transform opacity-100 scale-100" | |
x-transition:leave-end="transform opacity-0 scale-95" | |
> | |
Hello there | |
</div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { transitionIn, transitionOut } from 'bare_transitions' | |
import { Controller } from 'stimulus' | |
export default class extends Controller { | |
toggle () { | |
if (this.toggleAreaIsHidden()) { | |
transitionIn(this.toggleAreaTarget, () => this.toggleAreaTarget.classList.remove(this.hiddenClass)) | |
} else { | |
transitionOut(this.toggleAreaTarget, () => this.toggleAreaTarget.classList.add(this.hiddenClass)) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment