Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/alpinejs/alpine into jlb/co…
Browse files Browse the repository at this point in the history
…mbobox
  • Loading branch information
SimoTod committed Feb 11, 2023
2 parents 6d02e22 + 4751093 commit d4534b7
Show file tree
Hide file tree
Showing 24 changed files with 103 additions and 32 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '15'
node-version: '18'
- run: npm install
- run: npm run build
- run: npm run test
2 changes: 1 addition & 1 deletion packages/alpinejs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "alpinejs",
"version": "3.10.5",
"version": "3.11.1",
"description": "The rugged, minimal JavaScript framework",
"homepage": "https://alpinejs.dev",
"repository": {
Expand Down
2 changes: 0 additions & 2 deletions packages/alpinejs/src/alpine.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { getBinding as bound, extractProp } from './utils/bind'
import { debounce } from './utils/debounce'
import { throttle } from './utils/throttle'
import { setStyles } from './utils/styles'
import { entangle } from './entangle'
import { nextTick } from './nextTick'
import { walk } from './utils/walk'
import { plugin } from './plugin'
Expand Down Expand Up @@ -53,7 +52,6 @@ let Alpine = {
setStyles, // INTERNAL
mutateDom,
directive,
entangle,
throttle,
debounce,
evaluate,
Expand Down
2 changes: 1 addition & 1 deletion packages/alpinejs/src/clone.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { effect, release, overrideEffect } from "./reactivity"
import { initTree, isRoot } from "./lifecycle"
import { walk } from "./utils/walk"

let isCloning = false
export let isCloning = false

export function skipDuringClone(callback, fallback = () => {}) {
return (...args) => isCloning ? fallback(...args) : callback(...args)
Expand Down
2 changes: 1 addition & 1 deletion packages/alpinejs/src/directives/x-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ directive('data', skipDuringClone((el, { expression }, { cleanup }) => {

let data = evaluate(el, expression, { scope: dataProviderContext })

if (data === undefined) data = {}
if (data === undefined || data === true) data = {}

injectMagics(data, el)

Expand Down
8 changes: 6 additions & 2 deletions packages/alpinejs/src/directives/x-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { nextTick } from '../nextTick'
import bind from '../utils/bind'
import on from '../utils/on'
import { warn } from '../utils/warn'
import { isCloning } from '../clone'

directive('model', (el, { modifiers, expression }, { effect, cleanup }) => {
let scopeTarget = el
Expand Down Expand Up @@ -62,7 +63,10 @@ directive('model', (el, { modifiers, expression }, { effect, cleanup }) => {
|| modifiers.includes('lazy')
? 'change' : 'input'

let removeListener = on(el, event, modifiers, (e) => {
// We only want to register the event listener when we're not cloning, since the
// mutation observer handles initializing the x-model directive already when
// the element is inserted into the DOM. Otherwise we register it twice.
let removeListener = isCloning ? () => {} : on(el, event, modifiers, (e) => {
setValue(getInputValue(el, modifiers, e, getValue()))
})

Expand Down Expand Up @@ -127,7 +131,7 @@ function getInputValue(el, modifiers, event, currentValue) {
// Safari autofill triggers event as CustomEvent and assigns value to target
// so we return event.target.value instead of event.detail
if (event instanceof CustomEvent && event.detail !== undefined) {
return event.detail || event.target.value
return typeof event.detail != 'undefined' ? event.detail : event.target.value
} else if (el.type === 'checkbox') {
// If the data we are binding to is an array, toggle its value inside the array.
if (Array.isArray(currentValue)) {
Expand Down
2 changes: 1 addition & 1 deletion packages/alpinejs/src/entangle.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { effect, release } from './reactivity'

export function entangle({ get: outerGet, set: outerSet }, { get: innerGet, set: innerSet }) {
let firstRun = true
let outerHash, innerHash
let outerHash, innerHash, outerHashLatest, innerHashLatest

let reference = effect(() => {
let outer, inner
Expand Down
8 changes: 3 additions & 5 deletions packages/alpinejs/src/evaluator.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,9 @@ export function normalEvaluator(el, expression) {

let dataStack = [overriddenMagics, ...closestDataStack(el)]

if (typeof expression === 'function') {
return generateEvaluatorFromFunction(dataStack, expression)
}

let evaluator = generateEvaluatorFromString(dataStack, expression, el)
let evaluator = (typeof expression === 'function')
? generateEvaluatorFromFunction(dataStack, expression)
: generateEvaluatorFromString(dataStack, expression, el)

return evaluator
return tryCatch.bind(null, el, expression, evaluator)
Expand Down
5 changes: 4 additions & 1 deletion packages/alpinejs/src/scheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
let flushPending = false
let flushing = false
let queue = []
let lastFlushedIndex = -1

export function scheduler (callback) { queueJob(callback) }

Expand All @@ -13,7 +14,7 @@ function queueJob(job) {
export function dequeueJob(job) {
let index = queue.indexOf(job)

if (index !== -1) queue.splice(index, 1)
if (index !== -1 && index > lastFlushedIndex) queue.splice(index, 1)
}

function queueFlush() {
Expand All @@ -30,9 +31,11 @@ export function flushJobs() {

for (let i = 0; i < queue.length; i++) {
queue[i]()
lastFlushedIndex = i
}

queue.length = 0
lastFlushedIndex = -1

flushing = false
}
2 changes: 1 addition & 1 deletion packages/collapse/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alpinejs/collapse",
"version": "3.10.5",
"version": "3.11.1",
"description": "Collapse and expand elements with robust animations",
"homepage": "https://alpinejs.dev/plugins/collapse",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alpinejs/docs",
"version": "3.10.5-revision.1",
"version": "3.11.1-revision.1",
"description": "The documentation for Alpine",
"author": "Caleb Porzio",
"license": "MIT"
Expand Down
4 changes: 2 additions & 2 deletions packages/docs/src/en/advanced/csp.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ In order for Alpine to be able to execute plain strings from HTML attributes as

> Under the hood, Alpine doesn't actually use eval() itself because it's slow and problematic. Instead it uses Function declarations, which are much better, but still violate "unsafe-eval".
In order to accommodate environments where this CSP is necessary, Alpine offers an alternate build that doesn't violate "unsafe-eval", but has a more restrictive syntax.
In order to accommodate environments where this CSP is necessary, Alpine will offer an alternate build that doesn't violate "unsafe-eval", but has a more restrictive syntax.

<a name="installation"></a>
## Installation

Like all Alpine extensions, you can include this either via `<script>` tag or module import:
The CSP build hasn’t been officially released yet. In the meantime, you may [build it from source](https://github.com/alpinejs/alpine/tree/main/packages/csp). Once released, like all Alpine extensions, you will be able to include this either via `<script>` tag or module import:

<a name="script-tag"></a>
### Script tag
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/advanced/extending.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ Now if the directive is removed from this element or the element is removed itse
### Custom order

By default, any new directive will run after the majority of the standard ones (with the exception of `x-teleport`). This is usually acceptable but some times you might need to run your custom directive before another specific one.
This can be achieved by chaining the `.before() function to `Alpine.directive()` and specifing which directive needs to run after your custom one.
This can be achieved by chaining the `.before() function to `Alpine.directive()` and specifying which directive needs to run after your custom one.

```js
Alpine.directive('foo', (el, { value, modifiers, expression }) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/essentials/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ This is by far the simplest way to get started with Alpine. Include the followin
Notice the `@3.x.x` in the provided CDN link. This will pull the latest version of Alpine version 3. For stability in production, it's recommended that you hardcode the latest version in the CDN link.

```alpine
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.10.5/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.11.1/dist/cdn.min.js"></script>
```

That's it! Alpine is now available for use inside your page.
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/essentials/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ document.addEventListener('alpine:init', () => {
<a name="alpine-initialized"></a>
### `alpine:initialized`

Alpine also offers a hook that you can use to execute code After it's done initializing called `alpine:initialized`:
Alpine also offers a hook that you can use to execute code AFTER it's done initializing called `alpine:initialized`:

```js
document.addEventListener('alpine:initialized', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/focus/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alpinejs/focus",
"version": "3.10.5",
"version": "3.11.1",
"description": "Manage focus within a page",
"homepage": "https://alpinejs.dev/plugins/focus",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/intersect/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alpinejs/intersect",
"version": "3.10.5",
"version": "3.11.1",
"description": "Trigger JavaScript when an element enters the viewport",
"homepage": "https://alpinejs.dev/plugins/intersect",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/mask/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alpinejs/mask",
"version": "3.10.5",
"version": "3.11.1",
"description": "An Alpine plugin for input masking",
"homepage": "https://alpinejs.dev/plugins/mask",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/morph/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alpinejs/morph",
"version": "3.10.5",
"version": "3.11.1",
"description": "Diff and patch a block of HTML on a page with an HTML template",
"homepage": "https://alpinejs.dev/plugins/morph",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/persist/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alpinejs/persist",
"version": "3.10.5",
"version": "3.11.1",
"description": "Persist Alpine data across page loads",
"homepage": "https://alpinejs.dev/plugins/persist",
"repository": {
Expand Down
31 changes: 31 additions & 0 deletions tests/cypress/integration/clone.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,34 @@ test('wont register listeners on clone',
get('#copy span').should(haveText('1'))
}
)

test('wont register extra listeners on x-model on clone',
html`
<script>
document.addEventListener('alpine:initialized', () => {
window.original = document.getElementById('original')
window.copy = document.getElementById('copy')
})
</script>
<button x-data @click="Alpine.clone(original, copy)">click</button>
<div x-data="{ checks: [] }" id="original">
<input type="checkbox" x-model="checks" value="1">
<span x-text="checks"></span>
</div>
<div x-data="{ checks: [] }" id="copy">
<input type="checkbox" x-model="checks" value="1">
<span x-text="checks"></span>
</div>
`,
({ get }) => {
get('#original span').should(haveText(''))
get('#copy span').should(haveText(''))
get('button').click()
get('#copy span').should(haveText(''))
get('#copy input').click()
get('#copy span').should(haveText('1'))
}
)
37 changes: 37 additions & 0 deletions tests/cypress/integration/directives/x-if.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,40 @@ test('x-if removed dom does not evaluate reactive expressions in dom tree',
get('span').should(notExist())
}
)

// Attempting to skip an already-flushed reactive effect would cause inconsistencies when updating other effects.
// See https://github.com/alpinejs/alpine/issues/2803 for more details.
test('x-if removed dom does not attempt skipping already-processed reactive effects in dom tree',
html`
<div x-data="{
isEditing: true,
foo: 'random text',
stopEditing() {
this.foo = '';
this.isEditing = false;
},
}">
<button @click="stopEditing">Stop editing</button>
<template x-if="isEditing">
<div id="div-editing">
<h2>Editing</h2>
<input id="foo" name="foo" type="text" x-model="foo" />
</div>
</template>
<template x-if="!isEditing">
<div id="div-not-editing"><h2>Not editing</h2></div>
</template>
<template x-if="!isEditing">
<div id="div-also-not-editing"><h2>Also not editing</h2></div>
</template>
</div>
`,
({ get }) => {
get('button').click()
get('div#div-editing').should(notExist())
get('div#div-not-editing').should(exist())
get('div#div-also-not-editing').should(exist())
}
)
4 changes: 2 additions & 2 deletions tests/cypress/integration/entangle.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { haveValue, html, test } from '../utils'

test('can entangle to getter/setter pairs',
test.skip('can entangle to getter/setter pairs',
[html`
<div x-data="{ outer: 'foo' }">
<input x-model="outer" outer>
Expand Down Expand Up @@ -33,7 +33,7 @@ test('can entangle to getter/setter pairs',
}
)

test('can release entanglement',
test.skip('can release entanglement',
[html`
<div x-data="{ outer: 'foo' }">
<input x-model="outer" outer>
Expand Down
2 changes: 1 addition & 1 deletion tests/cypress/integration/plugins/navigate.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { beEqualTo, beVisible, haveAttribute, haveFocus, haveText, html, notBeVisible, test } from '../../utils'

// Test persistant peice of layout
// Test persistent piece of layout
// Handle non-origin links and such
// Handle 404
// Middle/command click link in new tab works?
Expand Down

0 comments on commit d4534b7

Please sign in to comment.