first commit
This commit is contained in:
235
public/assets/alpinejs/src/utils/bind.js
Normal file
235
public/assets/alpinejs/src/utils/bind.js
Normal file
@@ -0,0 +1,235 @@
|
||||
import { dontAutoEvaluateFunctions, evaluate } from '../evaluator'
|
||||
import { reactive } from '../reactivity'
|
||||
import { setClasses } from './classes'
|
||||
import { setStyles } from './styles'
|
||||
|
||||
export default function bind(el, name, value, modifiers = []) {
|
||||
// Register bound data as pure observable data for other APIs to use.
|
||||
if (! el._x_bindings) el._x_bindings = reactive({})
|
||||
|
||||
el._x_bindings[name] = value
|
||||
|
||||
name = modifiers.includes('camel') ? camelCase(name) : name
|
||||
|
||||
switch (name) {
|
||||
case 'value':
|
||||
bindInputValue(el, value)
|
||||
break;
|
||||
|
||||
case 'style':
|
||||
bindStyles(el, value)
|
||||
break;
|
||||
|
||||
case 'class':
|
||||
bindClasses(el, value)
|
||||
break;
|
||||
|
||||
// 'selected' and 'checked' are special attributes that aren't necessarily
|
||||
// synced with their corresponding properties when updated, so both the
|
||||
// attribute and property need to be updated when bound.
|
||||
case 'selected':
|
||||
case 'checked':
|
||||
bindAttributeAndProperty(el, name, value)
|
||||
break;
|
||||
|
||||
default:
|
||||
bindAttribute(el, name, value)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function bindInputValue(el, value) {
|
||||
if (isRadio(el)) {
|
||||
// Set radio value from x-bind:value, if no "value" attribute exists.
|
||||
// If there are any initial state values, radio will have a correct
|
||||
// "checked" value since x-bind:value is processed before x-model.
|
||||
if (el.attributes.value === undefined) {
|
||||
el.value = value
|
||||
}
|
||||
|
||||
// @todo: yuck
|
||||
if (window.fromModel) {
|
||||
if (typeof value === 'boolean') {
|
||||
el.checked = safeParseBoolean(el.value) === value
|
||||
} else {
|
||||
el.checked = checkedAttrLooseCompare(el.value, value)
|
||||
}
|
||||
}
|
||||
} else if (isCheckbox(el)) {
|
||||
// If we are explicitly binding a string to the :value, set the string,
|
||||
// If the value is a boolean/array/number/null/undefined, leave it alone, it will be set to "on"
|
||||
// automatically.
|
||||
if (Number.isInteger(value)) {
|
||||
el.value = value
|
||||
} else if (! Array.isArray(value) && typeof value !== 'boolean' && ! [null, undefined].includes(value)) {
|
||||
el.value = String(value)
|
||||
} else {
|
||||
if (Array.isArray(value)) {
|
||||
el.checked = value.some(val => checkedAttrLooseCompare(val, el.value))
|
||||
} else {
|
||||
el.checked = !!value
|
||||
}
|
||||
}
|
||||
} else if (el.tagName === 'SELECT') {
|
||||
updateSelect(el, value)
|
||||
} else {
|
||||
if (el.value === value) return
|
||||
|
||||
el.value = value === undefined ? '' : value
|
||||
}
|
||||
}
|
||||
|
||||
function bindClasses(el, value) {
|
||||
if (el._x_undoAddedClasses) el._x_undoAddedClasses()
|
||||
|
||||
el._x_undoAddedClasses = setClasses(el, value)
|
||||
}
|
||||
|
||||
function bindStyles(el, value) {
|
||||
if (el._x_undoAddedStyles) el._x_undoAddedStyles()
|
||||
|
||||
el._x_undoAddedStyles = setStyles(el, value)
|
||||
}
|
||||
|
||||
function bindAttributeAndProperty(el, name, value) {
|
||||
bindAttribute(el, name, value)
|
||||
setPropertyIfChanged(el, name, value)
|
||||
}
|
||||
|
||||
function bindAttribute(el, name, value) {
|
||||
if ([null, undefined, false].includes(value) && attributeShouldntBePreservedIfFalsy(name)) {
|
||||
el.removeAttribute(name)
|
||||
} else {
|
||||
if (isBooleanAttr(name)) value = name
|
||||
|
||||
setIfChanged(el, name, value)
|
||||
}
|
||||
}
|
||||
|
||||
function setIfChanged(el, attrName, value) {
|
||||
if (el.getAttribute(attrName) != value) {
|
||||
el.setAttribute(attrName, value)
|
||||
}
|
||||
}
|
||||
|
||||
function setPropertyIfChanged(el, propName, value) {
|
||||
if (el[propName] !== value) {
|
||||
el[propName] = value
|
||||
}
|
||||
}
|
||||
|
||||
function updateSelect(el, value) {
|
||||
const arrayWrappedValue = [].concat(value).map(value => { return value + '' })
|
||||
|
||||
Array.from(el.options).forEach(option => {
|
||||
option.selected = arrayWrappedValue.includes(option.value)
|
||||
})
|
||||
}
|
||||
|
||||
function camelCase(subject) {
|
||||
return subject.toLowerCase().replace(/-(\w)/g, (match, char) => char.toUpperCase())
|
||||
}
|
||||
|
||||
function checkedAttrLooseCompare(valueA, valueB) {
|
||||
return valueA == valueB
|
||||
}
|
||||
|
||||
export function safeParseBoolean(rawValue) {
|
||||
if ([1, '1', 'true', 'on', 'yes', true].includes(rawValue)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if ([0, '0', 'false', 'off', 'no', false].includes(rawValue)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return rawValue ? Boolean(rawValue) : null
|
||||
}
|
||||
|
||||
// As per HTML spec table https://html.spec.whatwg.org/multipage/indices.html#attributes-3:boolean-attribute
|
||||
const booleanAttributes = new Set([
|
||||
'allowfullscreen',
|
||||
'async',
|
||||
'autofocus',
|
||||
'autoplay',
|
||||
'checked',
|
||||
'controls',
|
||||
'default',
|
||||
'defer',
|
||||
'disabled',
|
||||
'formnovalidate',
|
||||
'inert',
|
||||
'ismap',
|
||||
'itemscope',
|
||||
'loop',
|
||||
'multiple',
|
||||
'muted',
|
||||
'nomodule',
|
||||
'novalidate',
|
||||
'open',
|
||||
'playsinline',
|
||||
'readonly',
|
||||
'required',
|
||||
'reversed',
|
||||
'selected',
|
||||
'shadowrootclonable',
|
||||
'shadowrootdelegatesfocus',
|
||||
'shadowrootserializable',
|
||||
])
|
||||
|
||||
function isBooleanAttr(attrName) {
|
||||
return booleanAttributes.has(attrName)
|
||||
}
|
||||
|
||||
function attributeShouldntBePreservedIfFalsy(name) {
|
||||
return ! ['aria-pressed', 'aria-checked', 'aria-expanded', 'aria-selected'].includes(name)
|
||||
}
|
||||
|
||||
export function getBinding(el, name, fallback) {
|
||||
// First let's get it out of Alpine bound data.
|
||||
if (el._x_bindings && el._x_bindings[name] !== undefined) return el._x_bindings[name]
|
||||
|
||||
return getAttributeBinding(el, name, fallback)
|
||||
}
|
||||
|
||||
export function extractProp(el, name, fallback, extract = true) {
|
||||
// First let's get it out of Alpine bound data.
|
||||
if (el._x_bindings && el._x_bindings[name] !== undefined) return el._x_bindings[name]
|
||||
|
||||
if (el._x_inlineBindings && el._x_inlineBindings[name] !== undefined) {
|
||||
let binding = el._x_inlineBindings[name]
|
||||
|
||||
binding.extract = extract
|
||||
|
||||
return dontAutoEvaluateFunctions(() => {
|
||||
return evaluate(el, binding.expression)
|
||||
})
|
||||
}
|
||||
|
||||
return getAttributeBinding(el, name, fallback)
|
||||
}
|
||||
|
||||
function getAttributeBinding(el, name, fallback) {
|
||||
// If not, we'll return the literal attribute.
|
||||
let attr = el.getAttribute(name)
|
||||
|
||||
// Nothing bound:
|
||||
if (attr === null) return typeof fallback === 'function' ? fallback() : fallback
|
||||
|
||||
// The case of a custom attribute with no value. Ex: <div manual>
|
||||
if (attr === '') return true
|
||||
|
||||
if (isBooleanAttr(name)) {
|
||||
return !! [name, 'true'].includes(attr)
|
||||
}
|
||||
|
||||
return attr
|
||||
}
|
||||
|
||||
export function isCheckbox(el) {
|
||||
return el.type === 'checkbox' || el.localName === 'ui-checkbox' || el.localName === 'ui-switch'
|
||||
}
|
||||
|
||||
export function isRadio(el) {
|
||||
return el.type === 'radio' || el.localName === 'ui-radio'
|
||||
}
|
||||
58
public/assets/alpinejs/src/utils/classes.js
Normal file
58
public/assets/alpinejs/src/utils/classes.js
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
export function setClasses(el, value) {
|
||||
if (Array.isArray(value)) {
|
||||
return setClassesFromString(el, value.join(' '))
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
return setClassesFromObject(el, value)
|
||||
} else if (typeof value === 'function') {
|
||||
return setClasses(el, value())
|
||||
}
|
||||
|
||||
return setClassesFromString(el, value)
|
||||
}
|
||||
|
||||
function setClassesFromString(el, classString) {
|
||||
let split = classString => classString.split(' ').filter(Boolean)
|
||||
|
||||
let missingClasses = classString => classString.split(' ').filter(i => ! el.classList.contains(i)).filter(Boolean)
|
||||
|
||||
let addClassesAndReturnUndo = classes => {
|
||||
el.classList.add(...classes)
|
||||
|
||||
return () => { el.classList.remove(...classes) }
|
||||
}
|
||||
|
||||
// This is to allow short-circuit expressions like: :class="show || 'hidden'" && "show && 'block'"
|
||||
classString = (classString === true) ? classString = '' : (classString || '')
|
||||
|
||||
return addClassesAndReturnUndo(missingClasses(classString))
|
||||
}
|
||||
|
||||
function setClassesFromObject(el, classObject) {
|
||||
let split = classString => classString.split(' ').filter(Boolean)
|
||||
|
||||
let forAdd = Object.entries(classObject).flatMap(([classString, bool]) => bool ? split(classString) : false).filter(Boolean)
|
||||
let forRemove = Object.entries(classObject).flatMap(([classString, bool]) => ! bool ? split(classString) : false).filter(Boolean)
|
||||
|
||||
let added = []
|
||||
let removed = []
|
||||
|
||||
forRemove.forEach(i => {
|
||||
if (el.classList.contains(i)) {
|
||||
el.classList.remove(i)
|
||||
removed.push(i)
|
||||
}
|
||||
})
|
||||
|
||||
forAdd.forEach(i => {
|
||||
if (! el.classList.contains(i)) {
|
||||
el.classList.add(i)
|
||||
added.push(i)
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
removed.forEach(i => el.classList.add(i))
|
||||
added.forEach(i => el.classList.remove(i))
|
||||
}
|
||||
}
|
||||
18
public/assets/alpinejs/src/utils/debounce.js
Normal file
18
public/assets/alpinejs/src/utils/debounce.js
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
export function debounce(func, wait) {
|
||||
let timeout
|
||||
|
||||
return function() {
|
||||
const context = this, args = arguments
|
||||
|
||||
const later = function () {
|
||||
timeout = null
|
||||
|
||||
func.apply(context, args)
|
||||
}
|
||||
|
||||
clearTimeout(timeout)
|
||||
|
||||
timeout = setTimeout(later, wait)
|
||||
}
|
||||
}
|
||||
12
public/assets/alpinejs/src/utils/dispatch.js
Normal file
12
public/assets/alpinejs/src/utils/dispatch.js
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
export function dispatch(el, name, detail = {}) {
|
||||
el.dispatchEvent(
|
||||
new CustomEvent(name, {
|
||||
detail,
|
||||
bubbles: true,
|
||||
// Allows events to pass the shadow DOM barrier.
|
||||
composed: true,
|
||||
cancelable: true,
|
||||
})
|
||||
)
|
||||
}
|
||||
27
public/assets/alpinejs/src/utils/error.js
Normal file
27
public/assets/alpinejs/src/utils/error.js
Normal file
@@ -0,0 +1,27 @@
|
||||
export function tryCatch(el, expression, callback, ...args) {
|
||||
try {
|
||||
return callback(...args)
|
||||
} catch (e) {
|
||||
handleError( e, el, expression )
|
||||
}
|
||||
}
|
||||
|
||||
export function handleError(...args) {
|
||||
return errorHandler(...args)
|
||||
}
|
||||
|
||||
let errorHandler = normalErrorHandler
|
||||
|
||||
export function setErrorHandler(handler) {
|
||||
errorHandler = handler
|
||||
}
|
||||
|
||||
export function normalErrorHandler(error , el, expression = undefined) {
|
||||
error = Object.assign(
|
||||
error ?? { message: 'No error message given.' },
|
||||
{ el, expression } )
|
||||
|
||||
console.warn(`Alpine Expression Error: ${error.message}\n\n${ expression ? 'Expression: \"' + expression + '\"\n\n' : '' }`, el)
|
||||
|
||||
setTimeout( () => { throw error }, 0 )
|
||||
}
|
||||
206
public/assets/alpinejs/src/utils/on.js
Normal file
206
public/assets/alpinejs/src/utils/on.js
Normal file
@@ -0,0 +1,206 @@
|
||||
import { debounce } from './debounce'
|
||||
import { throttle } from './throttle'
|
||||
|
||||
export default function on (el, event, modifiers, callback) {
|
||||
let listenerTarget = el
|
||||
|
||||
let handler = e => callback(e)
|
||||
|
||||
let options = {}
|
||||
|
||||
// This little helper allows us to add functionality to the listener's
|
||||
// handler more flexibly in a "middleware" style.
|
||||
let wrapHandler = (callback, wrapper) => (e) => wrapper(callback, e)
|
||||
|
||||
if (modifiers.includes("dot")) event = dotSyntax(event)
|
||||
if (modifiers.includes('camel')) event = camelCase(event)
|
||||
if (modifiers.includes('passive')) options.passive = true
|
||||
if (modifiers.includes('capture')) options.capture = true
|
||||
if (modifiers.includes('window')) listenerTarget = window
|
||||
if (modifiers.includes('document')) listenerTarget = document
|
||||
|
||||
// By wrapping the handler with debounce & throttle first, we ensure that the wrapping logic itself is not
|
||||
// throttled/debounced, only the user's callback is. This way, if the user expects
|
||||
// `e.preventDefault()` to happen, it'll still happen even if their callback gets throttled.
|
||||
if (modifiers.includes('debounce')) {
|
||||
let nextModifier = modifiers[modifiers.indexOf('debounce')+1] || 'invalid-wait'
|
||||
let wait = isNumeric(nextModifier.split('ms')[0]) ? Number(nextModifier.split('ms')[0]) : 250
|
||||
|
||||
handler = debounce(handler, wait)
|
||||
}
|
||||
if (modifiers.includes('throttle')) {
|
||||
let nextModifier = modifiers[modifiers.indexOf('throttle')+1] || 'invalid-wait'
|
||||
let wait = isNumeric(nextModifier.split('ms')[0]) ? Number(nextModifier.split('ms')[0]) : 250
|
||||
|
||||
handler = throttle(handler, wait)
|
||||
}
|
||||
|
||||
if (modifiers.includes('prevent')) handler = wrapHandler(handler, (next, e) => { e.preventDefault(); next(e) })
|
||||
if (modifiers.includes('stop')) handler = wrapHandler(handler, (next, e) => { e.stopPropagation(); next(e) })
|
||||
|
||||
if (modifiers.includes("once")) {
|
||||
handler = wrapHandler(handler, (next, e) => {
|
||||
next(e);
|
||||
|
||||
listenerTarget.removeEventListener(event, handler, options);
|
||||
});
|
||||
}
|
||||
|
||||
if (modifiers.includes('away') || modifiers.includes('outside')) {
|
||||
listenerTarget = document
|
||||
|
||||
handler = wrapHandler(handler, (next, e) => {
|
||||
if (el.contains(e.target)) return
|
||||
|
||||
if (e.target.isConnected === false) return
|
||||
|
||||
if (el.offsetWidth < 1 && el.offsetHeight < 1) return
|
||||
|
||||
// Additional check for special implementations like x-collapse
|
||||
// where the element doesn't have display: none
|
||||
if (el._x_isShown === false) return
|
||||
|
||||
next(e)
|
||||
})
|
||||
}
|
||||
|
||||
if (modifiers.includes('self')) handler = wrapHandler(handler, (next, e) => { e.target === el && next(e) })
|
||||
|
||||
// Flush any pending model updates before submit handlers run
|
||||
// (e.g. x-model.blur inputs that haven't synced yet).
|
||||
if (event === 'submit') {
|
||||
handler = wrapHandler(handler, (next, e) => {
|
||||
if (e.target._x_pendingModelUpdates) {
|
||||
e.target._x_pendingModelUpdates.forEach(fn => fn())
|
||||
}
|
||||
|
||||
next(e)
|
||||
})
|
||||
}
|
||||
|
||||
// Handle :keydown and :keyup listeners.
|
||||
// Handle :click and :auxclick listeners.
|
||||
if (isKeyEvent(event) || isClickEvent(event)) {
|
||||
handler = wrapHandler(handler, (next, e) => {
|
||||
if (isListeningForASpecificKeyThatHasntBeenPressed(e, modifiers)) {
|
||||
return
|
||||
}
|
||||
|
||||
next(e)
|
||||
})
|
||||
}
|
||||
|
||||
listenerTarget.addEventListener(event, handler, options)
|
||||
|
||||
return () => {
|
||||
listenerTarget.removeEventListener(event, handler, options)
|
||||
}
|
||||
}
|
||||
|
||||
function dotSyntax(subject) {
|
||||
return subject.replace(/-/g, ".")
|
||||
}
|
||||
|
||||
function camelCase(subject) {
|
||||
return subject.toLowerCase().replace(/-(\w)/g, (match, char) => char.toUpperCase())
|
||||
}
|
||||
|
||||
function isNumeric(subject){
|
||||
return ! Array.isArray(subject) && ! isNaN(subject)
|
||||
}
|
||||
|
||||
function kebabCase(subject) {
|
||||
if ([' ','_'].includes(subject
|
||||
)) return subject
|
||||
return subject.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[_\s]/, '-').toLowerCase()
|
||||
}
|
||||
|
||||
function isKeyEvent(event) {
|
||||
return ['keydown', 'keyup'].includes(event)
|
||||
}
|
||||
|
||||
function isClickEvent(event) {
|
||||
return ['contextmenu','click','mouse'].some(i => event.includes(i))
|
||||
}
|
||||
|
||||
function isListeningForASpecificKeyThatHasntBeenPressed(e, modifiers) {
|
||||
let keyModifiers = modifiers.filter(i => {
|
||||
// `preserve-scroll` is specifically for Livewire and is not used by Alpine...
|
||||
// `blur`, `change`, `enter`, `lazy` are x-model event trigger modifiers, not key modifiers
|
||||
return ! ['window', 'document', 'prevent', 'stop', 'once', 'capture', 'self', 'away', 'outside', 'passive', 'preserve-scroll', 'blur', 'change', 'lazy'].includes(i)
|
||||
})
|
||||
|
||||
if (keyModifiers.includes('debounce')) {
|
||||
let debounceIndex = keyModifiers.indexOf('debounce')
|
||||
keyModifiers.splice(debounceIndex, isNumeric((keyModifiers[debounceIndex+1] || 'invalid-wait').split('ms')[0]) ? 2 : 1)
|
||||
}
|
||||
|
||||
if (keyModifiers.includes('throttle')) {
|
||||
let debounceIndex = keyModifiers.indexOf('throttle')
|
||||
keyModifiers.splice(debounceIndex, isNumeric((keyModifiers[debounceIndex+1] || 'invalid-wait').split('ms')[0]) ? 2 : 1)
|
||||
}
|
||||
|
||||
// If no modifier is specified, we'll call it a press.
|
||||
if (keyModifiers.length === 0) return false
|
||||
|
||||
// If one is passed, AND it matches the key pressed, we'll call it a press.
|
||||
if (keyModifiers.length === 1 && keyToModifiers(e.key).includes(keyModifiers[0])) return false
|
||||
|
||||
// The user is listening for key combinations.
|
||||
const systemKeyModifiers = ['ctrl', 'shift', 'alt', 'meta', 'cmd', 'super']
|
||||
const selectedSystemKeyModifiers = systemKeyModifiers.filter(modifier => keyModifiers.includes(modifier))
|
||||
|
||||
keyModifiers = keyModifiers.filter(i => ! selectedSystemKeyModifiers.includes(i))
|
||||
|
||||
if (selectedSystemKeyModifiers.length > 0) {
|
||||
const activelyPressedKeyModifiers = selectedSystemKeyModifiers.filter(modifier => {
|
||||
// Alias "cmd" and "super" to "meta"
|
||||
if (modifier === 'cmd' || modifier === 'super') modifier = 'meta'
|
||||
|
||||
return e[`${modifier}Key`]
|
||||
})
|
||||
|
||||
// If all the modifiers selected are pressed, ...
|
||||
if (activelyPressedKeyModifiers.length === selectedSystemKeyModifiers.length) {
|
||||
|
||||
// AND the event is a click. It's a pass.
|
||||
if (isClickEvent(e.type)) return false
|
||||
|
||||
// OR the remaining key is pressed as well. It's a press.
|
||||
if (keyToModifiers(e.key).includes(keyModifiers[0])) return false
|
||||
}
|
||||
}
|
||||
|
||||
// We'll call it NOT a valid keypress.
|
||||
return true
|
||||
}
|
||||
|
||||
function keyToModifiers(key) {
|
||||
if (! key) return []
|
||||
|
||||
key = kebabCase(key)
|
||||
|
||||
let modifierToKeyMap = {
|
||||
'ctrl': 'control',
|
||||
'slash': '/',
|
||||
'space': ' ',
|
||||
'spacebar': ' ',
|
||||
'cmd': 'meta',
|
||||
'esc': 'escape',
|
||||
'up': 'arrow-up',
|
||||
'down': 'arrow-down',
|
||||
'left': 'arrow-left',
|
||||
'right': 'arrow-right',
|
||||
'period': '.',
|
||||
'comma': ',',
|
||||
'equal': '=',
|
||||
'minus': '-',
|
||||
'underscore': '_',
|
||||
}
|
||||
|
||||
modifierToKeyMap[key] = key
|
||||
|
||||
return Object.keys(modifierToKeyMap).map(modifier => {
|
||||
if (modifierToKeyMap[modifier] === key) return modifier
|
||||
}).filter(modifier => modifier)
|
||||
}
|
||||
14
public/assets/alpinejs/src/utils/once.js
Normal file
14
public/assets/alpinejs/src/utils/once.js
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
export function once(callback, fallback = () => {}) {
|
||||
let called = false
|
||||
|
||||
return function () {
|
||||
if (! called) {
|
||||
called = true
|
||||
|
||||
callback.apply(this, arguments)
|
||||
} else {
|
||||
fallback.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
50
public/assets/alpinejs/src/utils/styles.js
Normal file
50
public/assets/alpinejs/src/utils/styles.js
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
export function setStyles(el, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
return setStylesFromObject(el, value)
|
||||
}
|
||||
|
||||
return setStylesFromString(el, value)
|
||||
}
|
||||
|
||||
function setStylesFromObject(el, value) {
|
||||
let previousStyles = {}
|
||||
|
||||
Object.entries(value).forEach(([key, value]) => {
|
||||
previousStyles[key] = el.style[key]
|
||||
|
||||
// When we use javascript object, css properties use the camelCase
|
||||
// syntax but when we use setProperty, we need the css format
|
||||
// so we need to convert camelCase to kebab-case.
|
||||
// In case key is a CSS variable, leave it as it is.
|
||||
if (! key.startsWith('--')) {
|
||||
key = kebabCase(key);
|
||||
}
|
||||
|
||||
el.style.setProperty(key, value)
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
if (el.style.length === 0) {
|
||||
el.removeAttribute('style')
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
setStyles(el, previousStyles)
|
||||
}
|
||||
}
|
||||
|
||||
function setStylesFromString(el, value) {
|
||||
let cache = el.getAttribute('style', value)
|
||||
|
||||
el.setAttribute('style', value)
|
||||
|
||||
return () => {
|
||||
el.setAttribute('style', cache || '')
|
||||
}
|
||||
}
|
||||
|
||||
function kebabCase(subject) {
|
||||
return subject.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase()
|
||||
}
|
||||
16
public/assets/alpinejs/src/utils/throttle.js
Normal file
16
public/assets/alpinejs/src/utils/throttle.js
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
export function throttle(func, limit) {
|
||||
let inThrottle
|
||||
|
||||
return function() {
|
||||
let context = this, args = arguments
|
||||
|
||||
if (! inThrottle) {
|
||||
func.apply(context, args)
|
||||
|
||||
inThrottle = true
|
||||
|
||||
setTimeout(() => inThrottle = false, limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
21
public/assets/alpinejs/src/utils/walk.js
Normal file
21
public/assets/alpinejs/src/utils/walk.js
Normal file
@@ -0,0 +1,21 @@
|
||||
export function walk(el, callback) {
|
||||
if (typeof ShadowRoot === 'function' && el instanceof ShadowRoot) {
|
||||
Array.from(el.children).forEach(el => walk(el, callback))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let skip = false
|
||||
|
||||
callback(el, () => skip = true)
|
||||
|
||||
if (skip) return
|
||||
|
||||
let node = el.firstElementChild
|
||||
|
||||
while (node) {
|
||||
walk(node, callback, false)
|
||||
|
||||
node = node.nextElementSibling
|
||||
}
|
||||
}
|
||||
4
public/assets/alpinejs/src/utils/warn.js
Normal file
4
public/assets/alpinejs/src/utils/warn.js
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
export function warn(message, ...args) {
|
||||
console.warn(`Alpine Warning: ${message}`, ...args)
|
||||
}
|
||||
Reference in New Issue
Block a user