@alpinejs/docs 3.7.1-revision.1 → 3.8.1-revision.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alpinejs/docs",
3
- "version": "3.7.1-revision.1",
3
+ "version": "3.8.1-revision.1",
4
4
  "description": "The documentation for Alpine",
5
5
  "author": "Caleb Porzio",
6
6
  "license": "MIT"
@@ -157,9 +157,9 @@ And like most expressions in Alpine, you can always use the result of a JavaScri
157
157
  <a name="bind-directives"></a>
158
158
  ## Binding Alpine Directives Directly
159
159
 
160
- `x-bind` allows you to bind an object of different properties to an element.
160
+ `x-bind` allows you to bind an object of different directives and attributes to an element.
161
161
 
162
- The object keys are the directives (can be any directive including modifiers), and the values are callbacks to be evaluated by Alpine.
162
+ The object keys can be anything you would normally write as an attribute name in Alpine. This includes Alpine directives and modifiers, but also plain HTML attributes. The object values are either plain strings, or in the case of dynamic Alpine directoves, callbacks to be evaluated by Alpine.
163
163
 
164
164
  ```alpine
165
165
  <div x-data="dropdown()">
@@ -17,4 +17,4 @@ If this definition is confusing for you, that's ok. It's better explained throug
17
17
 
18
18
  When this component is loaded, the `x-effect` expression will be run and "Hello" will be logged into the console.
19
19
 
20
- Because Alpine knows about any property references contained within `x-effect`, when the button is clicked and `label` is changed", the effect will be re-triggered and "Hello World!" will be logged to the console.
20
+ Because Alpine knows about any property references contained within `x-effect`, when the button is clicked and `label` is changed, the effect will be re-triggered and "Hello World!" will be logged to the console.
@@ -18,7 +18,7 @@ This is useful for things like modals (especially nesting them), where it's help
18
18
 
19
19
  By attaching `x-teleport` to a `<template>` element, you are telling Alpine to "append" that element to the provided selector.
20
20
 
21
- > The `x-template` selector can be any string you would normally pass into something like `document.querySelector`. It will find the first element that matches, be it a tag name (`body`), class name (`.my-class`), ID (`#my-id`), or any other valid CSS selector.
21
+ > The `x-teleport` selector can be any string you would normally pass into something like `document.querySelector`. It will find the first element that matches, be it a tag name (`body`), class name (`.my-class`), ID (`#my-id`), or any other valid CSS selector.
22
22
 
23
23
  [→ Read more about `document.querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
24
24
 
@@ -33,7 +33,7 @@ This is by far the simplest way to get started with Alpine. Include the followin
33
33
  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.
34
34
 
35
35
  ```alpine
36
- <script defer src="https://unpkg.com/alpinejs@3.7.1/dist/cdn.min.js"></script>
36
+ <script defer src="https://unpkg.com/alpinejs@3.8.1/dist/cdn.min.js"></script>
37
37
  ```
38
38
 
39
39
  That's it! Alpine is now available for use inside your page.
@@ -303,7 +303,7 @@ Here is an example of a dynamically bound `class` attribute:
303
303
  As a shortcut, you can leave out the `x-bind` and use the shorthand `:` syntax directly:
304
304
 
305
305
  ```alpine
306
- <button ... :class="red ? 'bg-red' : ''>"
306
+ <button ... :class="red ? 'bg-red' : ''">
307
307
  ```
308
308
 
309
309
  Toggling classes on and off based on data inside Alpine is a common need. Here's an example of toggling a class using Alpine's `class` binding object syntax: (Note: this syntax is only available for `class` attributes)
@@ -0,0 +1,36 @@
1
+ ---
2
+ order: 3
3
+ title: bind()
4
+ ---
5
+
6
+ # Alpine.bind
7
+
8
+ `Alpine.bind(...)` provides a way to re-use [`x-bind`](/directives/bind#bind-directives) objects within your application.
9
+
10
+ Here's a simple example. Rather than binding attributes manually with Alpine:
11
+
12
+ ```alpine
13
+ <button type="button" @click="doSomething()" :disabled="shouldDisable"></button>
14
+ ```
15
+
16
+ You can bundle these attributes up into a reusable object and use `x-bind` to bind to that:
17
+
18
+ ```alpine
19
+ <button x-bind="SomeButton"></button>
20
+
21
+ <script>
22
+ document.addEventListener('alpine:init', () => {
23
+ Alpine.bind('SomeButton', () => ({
24
+ type: 'button',
25
+
26
+ '@click'() {
27
+ this.doSomething()
28
+ },
29
+
30
+ ':disabled'() {
31
+ return this.shouldDisable
32
+ },
33
+ }))
34
+ })
35
+ </script>
36
+ ```
@@ -3,7 +3,7 @@ order: 1
3
3
  title: data()
4
4
  ---
5
5
 
6
- # `Alpine.data`
6
+ # Alpine.data
7
7
 
8
8
  `Alpine.data(...)` provides a way to re-use `x-data` contexts within your application.
9
9
 
@@ -3,7 +3,7 @@ order: 2
3
3
  title: store()
4
4
  ---
5
5
 
6
- # `Alpine.store`
6
+ # Alpine.store
7
7
 
8
8
  Alpine offers global state management through the `Alpine.store()` API.
9
9
 
@@ -53,7 +53,7 @@ When the `<button>` is pressed, `foo.bar` will be set to "bob", and "{bar: 'bob'
53
53
 
54
54
  ```alpine
55
55
  <!-- 🚫 Infinite loop -->
56
- <div x-data="{ foo: { bar: 'baz', bob: 'lob' }}" x-init="$watch('foo', value => foo.bob = foo.bar">
56
+ <div x-data="{ foo: { bar: 'baz', bob: 'lob' }}" x-init="$watch('foo', value => foo.bob = foo.bar)">
57
57
  <button @click="foo.bar = 'bob'">Update</button>
58
58
  </div>
59
59
  ```
@@ -1,15 +1,17 @@
1
1
  ---
2
2
  order: 3
3
- title: Trap
4
- description: Easily trap page focus within an element (modals, dialogs, etc...)
5
- graph_image: https://alpinejs.dev/social_trap.jpg
3
+ title: Focus
4
+ description: Easily manage focus within the page
5
+ graph_image: https://alpinejs.dev/social_focus.jpg
6
6
  ---
7
7
 
8
- # Trap Plugin
8
+ > Notice: This Plugin was previously called "Trap". Trap's functionality has been absorbed into this plugin along with additional functionality. You can swap Trap for Focus without any breaking changes.
9
9
 
10
- Alpine's Trap plugin allows you to conditionally trap focus inside an element.
10
+ # Focus Plugin
11
11
 
12
- This is useful for modals and other dialog elements.
12
+ Alpine's Focus plugin allows you to manage focus on a page.
13
+
14
+ > This plugin internally makes heavy use of the open source tool: [Tabbable](https://github.com/focus-trap/tabbable). Big thanks to that team for providing a much needed solution to this problem.
13
15
 
14
16
  <a name="installation"></a>
15
17
  ## Installation
@@ -22,7 +24,7 @@ You can include the CDN build of this plugin as a `<script>` tag, just make sure
22
24
 
23
25
  ```alpine
24
26
  <!-- Alpine Plugins -->
25
- <script defer src="https://unpkg.com/@alpinejs/trap@3.x.x/dist/cdn.min.js"></script>
27
+ <script defer src="https://unpkg.com/@alpinejs/focus@3.x.x/dist/cdn.min.js"></script>
26
28
 
27
29
  <!-- Alpine Core -->
28
30
  <script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
@@ -30,19 +32,19 @@ You can include the CDN build of this plugin as a `<script>` tag, just make sure
30
32
 
31
33
  ### Via NPM
32
34
 
33
- You can install Trap from NPM for use inside your bundle like so:
35
+ You can install Focus from NPM for use inside your bundle like so:
34
36
 
35
37
  ```shell
36
- npm install @alpinejs/trap
38
+ npm install @alpinejs/focus
37
39
  ```
38
40
 
39
41
  Then initialize it from your bundle:
40
42
 
41
43
  ```js
42
44
  import Alpine from 'alpinejs'
43
- import trap from '@alpinejs/trap'
45
+ import focus from '@alpinejs/focus'
44
46
 
45
- Alpine.plugin(trap)
47
+ Alpine.plugin(focus)
46
48
 
47
49
  ...
48
50
  ```
@@ -50,7 +52,7 @@ Alpine.plugin(trap)
50
52
  <a name="x-trap"></a>
51
53
  ## x-trap
52
54
 
53
- The primary API for using this plugin is the `x-trap` directive.
55
+ Focus offers a dedicated API for trapping focus within an element: the `x-trap` directive.
54
56
 
55
57
  `x-trap` accepts a JS expression. If the result of that expression is true, then the focus will be trapped inside that element until the expression becomes false, then at that point, focus will be returned to where it was previously.
56
58
 
@@ -99,7 +101,7 @@ For example:
99
101
  <!-- END_VERBATIM -->
100
102
 
101
103
  <a name="nesting"></a>
102
- ## Nesting dialogs
104
+ ### Nesting dialogs
103
105
 
104
106
  Sometimes you may want to nest one dialog inside another. `x-trap` makes this trivial and handles it automatically.
105
107
 
@@ -180,10 +182,10 @@ Here is nesting in action:
180
182
  <!-- END_VERBATIM -->
181
183
 
182
184
  <a name="modifiers"></a>
183
- ## Modifiers
185
+ ### Modifiers
184
186
 
185
187
  <a name="inert"></a>
186
- ### .inert
188
+ #### .inert
187
189
 
188
190
  When building things like dialogs/modals, it's recommended to hide all the other elements on the page from screenreaders when trapping focus.
189
191
 
@@ -214,7 +216,7 @@ By adding `.inert` to `x-trap`, when focus is trapped, all other elements on the
214
216
  ```
215
217
 
216
218
  <a name="noscroll"></a>
217
- ### .noscroll
219
+ #### .noscroll
218
220
 
219
221
  When building dialogs/modals with Alpine, it's recommended that you disable scrollling for the surrounding content when the dialog is open.
220
222
 
@@ -251,3 +253,121 @@ For example:
251
253
  </div>
252
254
  </div>
253
255
  <!-- END_VERBATIM -->
256
+
257
+ <a name="focus-magic"></a>
258
+ ## $focus
259
+
260
+ This plugin offers many smaller utilities for managing focus within a page. These utilities are exposed via the `$focus` magic.
261
+
262
+ | Property | Description |
263
+ | --- | --- |
264
+ | `focus(el)` | Focus the passed element (handling annoyances internally: using nextTick, etc.) |
265
+ | `focusable(el)` | Detect weather or not an element is focusable |
266
+ | `focusables()` | Get all "focusable" elements within the current element |
267
+ | `focused()` | Get the currently focused element on the page |
268
+ | `lastFocused()` | Get the last focused element on the page |
269
+ | `within(el)` | Specify an element to scope the `$focus` magic to (the current element by default) |
270
+ | `first()` | Focus the first focusable element |
271
+ | `last()` | Focus the last focusable element |
272
+ | `next()` | Focus the next focusable element |
273
+ | `previous()` | Focus the previous focusable element |
274
+ | `noscroll()` | Prevent scrolling to the element about to be focused |
275
+ | `wrap()` | When retrieving "next" or "previous" use "wrap around" (ex. returning the first element if getting the "next" element of the last element) |
276
+ | `getFirst()` | Retrieve the first focusable element |
277
+ | `getLast()` | Retrieve the last focusable element |
278
+ | `getNext()` | Retrieve the next focusable element |
279
+ | `getPrevious()` | Retrieve the previous focusable element |
280
+
281
+ Let's walk through a few examples of these utilities in use. The example below allows the user to control focus within the group of buttons using the arrow keys. You can test this by clicking on a button, then using the arrow keys to move focus around:
282
+
283
+ ```alpine
284
+ <div
285
+ @keydown.right="$focus.next()"
286
+ @keydown.left="$focus.previous()"
287
+ >
288
+ <button>First</button>
289
+ <button>Second</button>
290
+ <button>Third</button>
291
+ </div>
292
+ ```
293
+
294
+ <!-- START_VERBATIM -->
295
+ <div class="demo">
296
+ <div
297
+ x-data
298
+ @keydown.right="$focus.next()"
299
+ @keydown.left="$focus.previous()"
300
+ >
301
+ <button class="focus:outline-none focus:ring-2 focus:ring-aqua-400">First</button>
302
+ <button class="focus:outline-none focus:ring-2 focus:ring-aqua-400">Second</button>
303
+ <button class="focus:outline-none focus:ring-2 focus:ring-aqua-400">Third</button>
304
+ </div>
305
+ (Click a button, then use the arrow keys to move left and right)
306
+ </div>
307
+ <!-- END_VERBATIM -->
308
+
309
+ Notice how if the last button is focused, pressing "right arrow" won't do anything. Let's add the `.wrap()` method so that focus "wraps around":
310
+
311
+ ```alpine
312
+ <div
313
+ @keydown.right="$focus.wrap().next()"
314
+ @keydown.left="$focus.wrap().previous()"
315
+ >
316
+ <button>First</button>
317
+ <button>Second</button>
318
+ <button>Third</button>
319
+ </div>
320
+ ```
321
+
322
+ <!-- START_VERBATIM -->
323
+ <div class="demo">
324
+ <div
325
+ x-data
326
+ @keydown.right="$focus.wrap().next()"
327
+ @keydown.left="$focus.wrap().previous()"
328
+ >
329
+ <button class="focus:outline-none focus:ring-2 focus:ring-aqua-400">First</button>
330
+ <button class="focus:outline-none focus:ring-2 focus:ring-aqua-400">Second</button>
331
+ <button class="focus:outline-none focus:ring-2 focus:ring-aqua-400">Third</button>
332
+ </div>
333
+ (Click a button, then use the arrow keys to move left and right)
334
+ </div>
335
+ <!-- END_VERBATIM -->
336
+
337
+ Now, let's add two buttons, one to focus the first element in the button group, and another focus the last element:
338
+
339
+ ```alpine
340
+ <button @click="$focus.within($refs.buttons).first()">Focus "First"</button>
341
+ <button @click="$focus.within($refs.buttons).last()">Focus "Last"</button>
342
+
343
+ <div
344
+ x-ref="buttons"
345
+ @keydown.right="$focus.wrap().next()"
346
+ @keydown.left="$focus.wrap().previous()"
347
+ >
348
+ <button>First</button>
349
+ <button>Second</button>
350
+ <button>Third</button>
351
+ </div>
352
+ ```
353
+
354
+ <!-- START_VERBATIM -->
355
+ <div class="demo" x-data>
356
+ <button @click="$focus.within($refs.buttons).first()">Focus "First"</button>
357
+ <button @click="$focus.within($refs.buttons).last()">Focus "Last"</button>
358
+
359
+ <hr class="mt-2 mb-2"/>
360
+
361
+ <div
362
+ x-ref="buttons"
363
+ @keydown.right="$focus.wrap().next()"
364
+ @keydown.left="$focus.wrap().previous()"
365
+ >
366
+ <button class="focus:outline-none focus:ring-2 focus:ring-aqua-400">First</button>
367
+ <button class="focus:outline-none focus:ring-2 focus:ring-aqua-400">Second</button>
368
+ <button class="focus:outline-none focus:ring-2 focus:ring-aqua-400">Third</button>
369
+ </div>
370
+ </div>
371
+ <!-- END_VERBATIM -->
372
+
373
+ Notice that we needed to add a `.within()` method for each button so that `$focus` knows to scope itself to a different element (the `div` wrapping the buttons).
@@ -21,10 +21,10 @@ The best way to understand its purpose is with the following interactive visuali
21
21
 
22
22
  <div class="flex w-full justify-between" style="padding-bottom: 1rem">
23
23
  <div class="w-1/2 px-4">
24
- <button @click="slide = (slide === 1) ? 13 : slide - 1" class="w-full bg-brand rounded-full text-center py-3 font-bold text-white">Previous</button>
24
+ <button @click="slide = (slide === 1) ? 13 : slide - 1" class="w-full bg-aqua-400 rounded-full text-center py-3 font-bold text-white">Previous</button>
25
25
  </div>
26
26
  <div class="w-1/2 px-4">
27
- <button @click="slide = (slide % 13) + 1" class="w-full bg-brand rounded-full text-center py-3 font-bold text-white">Next</button>
27
+ <button @click="slide = (slide % 13) + 1" class="w-full bg-aqua-400 rounded-full text-center py-3 font-bold text-white">Next</button>
28
28
  </div>
29
29
  </div>
30
30
  </div>