@ramstack/alpinegear-fragment 1.0.0-preview.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/README.md +88 -0
- package/alpinegear-fragment.esm.js +71 -0
- package/alpinegear-fragment.esm.min.js +1 -0
- package/alpinegear-fragment.js +76 -0
- package/alpinegear-fragment.min.js +1 -0
- package/package.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# @ramstack/alpinegear-fragment
|
|
2
|
+
|
|
3
|
+
`@ramstack/alpinegear-fragment` is a plugin for [Alpine.js](https://alpinejs.dev/) that provides the `x-fragment` directive.
|
|
4
|
+
|
|
5
|
+
This directive allows you to use multiple root elements in your templates, similar to the `Fragment` feature found in frameworks like `Vue.js` and `React`. It is particularly useful when you want to avoid wrapping elements in unnecessary container tags.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
### Using CDN
|
|
11
|
+
To include the CDN version of this plugin, add the following `<script>` tag before the core `alpine.js` file:
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<!-- alpine.js plugin -->
|
|
15
|
+
<script src="https://cdn.jsdelivr.net/npm/@ramstack/alpinegear-fragment@1/alpinegear-fragment.min.js" defer></script>
|
|
16
|
+
|
|
17
|
+
<!-- alpine.js -->
|
|
18
|
+
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Using NPM
|
|
22
|
+
Alternatively, you can install the plugin via `npm`:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install --save @ramstack/alpinegear-fragment
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then initialize it in your bundle:
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
import Alpine from "alpinejs";
|
|
32
|
+
import fragment from "@ramstack/alpinegear-fragment";
|
|
33
|
+
|
|
34
|
+
Alpine.plugin(fragment);
|
|
35
|
+
Alpine.start();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
With the `x-fragment` directive, you can use multiple root elements in your components without needing a wrapper container:
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<div x-data="{ show: true, message: 'Example' }">
|
|
43
|
+
<div>
|
|
44
|
+
<button @click="show = !show">Edit Message</button>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<template x-if="show">
|
|
48
|
+
<template x-fragment>
|
|
49
|
+
<label>Message:</label>
|
|
50
|
+
<input x-model="message" />
|
|
51
|
+
</template>
|
|
52
|
+
</template>
|
|
53
|
+
</div>
|
|
54
|
+
```
|
|
55
|
+
In this example, the `x-fragment` directive allows the `<label>` and `<input>` elements to exist side by side without a parent container.
|
|
56
|
+
|
|
57
|
+
### Using with `x-for`
|
|
58
|
+
The `x-fragment` directive can also be used with the directive `x-for`, giving you the flexibility to render multiple sibling elements for each iteration without wrapping them:
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<div x-data="{
|
|
62
|
+
items: [
|
|
63
|
+
{ id: 1, term: 'Item 1', description: 'Description 1' },
|
|
64
|
+
{ id: 2, term: 'Item 2', description: 'Description 2' },
|
|
65
|
+
{ id: 3, term: 'Item 3', description: 'Description 3' } ]
|
|
66
|
+
}">
|
|
67
|
+
<dl>
|
|
68
|
+
<template x-for="item in items" :key="item.id">
|
|
69
|
+
<template x-fragment>
|
|
70
|
+
<dt x-text="item.term"></dt>
|
|
71
|
+
<dd x-text="item.description"></dd>
|
|
72
|
+
</template>
|
|
73
|
+
</template>
|
|
74
|
+
</dl>
|
|
75
|
+
</div>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Source code
|
|
79
|
+
You can find the source code for this plugin on GitHub:
|
|
80
|
+
|
|
81
|
+
https://github.com/rameel/ramstack.alpinegear.js/tree/main/src/plugins/fragment
|
|
82
|
+
|
|
83
|
+
## Contributions
|
|
84
|
+
Bug reports and contributions are welcome.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
This package is released as open source under the **MIT License**.
|
|
88
|
+
See the [LICENSE](https://github.com/rameel/ramstack.alpinegear.js/blob/main/LICENSE) file for more details.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const warn = (...args) => console.warn("alpine-gear.js:", ...args);
|
|
2
|
+
const is_template = el => el instanceof HTMLTemplateElement;
|
|
3
|
+
const is_element = el => el.nodeType === Node.ELEMENT_NODE;
|
|
4
|
+
|
|
5
|
+
function anchor_block(el, template, { addScopeToNode, cleanup, initTree, mutateDom, scope = {} }) {
|
|
6
|
+
if (el._r_block) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
initialize();
|
|
11
|
+
|
|
12
|
+
let nodes = is_template(template)
|
|
13
|
+
? [...template.content.cloneNode(true).childNodes]
|
|
14
|
+
: [template.cloneNode(true)];
|
|
15
|
+
|
|
16
|
+
mutateDom(() => {
|
|
17
|
+
for (let node of nodes) {
|
|
18
|
+
is_element(node) && addScopeToNode(node, scope, el);
|
|
19
|
+
el.parentElement.insertBefore(node, el);
|
|
20
|
+
is_element(node) && initTree(node);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
el._r_block = {
|
|
25
|
+
template,
|
|
26
|
+
update() {
|
|
27
|
+
mutateDom(() => {
|
|
28
|
+
for (let node of nodes ?? []) {
|
|
29
|
+
el.parentElement.insertBefore(node, el);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
delete() {
|
|
34
|
+
el._r_block = null;
|
|
35
|
+
for (let node of nodes ?? []) {
|
|
36
|
+
node.remove();
|
|
37
|
+
}
|
|
38
|
+
nodes = null;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
cleanup(() => el._r_block?.delete());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function initialize() {
|
|
46
|
+
document.body._r_block ??= (() => {
|
|
47
|
+
const observer = new MutationObserver(mutations => {
|
|
48
|
+
for (let mutation of mutations) {
|
|
49
|
+
for (let node of mutation.addedNodes) {
|
|
50
|
+
node._r_block?.update();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
56
|
+
return observer;
|
|
57
|
+
})();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function plugin({ addScopeToNode, directive, initTree, mutateDom }) {
|
|
61
|
+
directive("fragment", (el, {}, { cleanup }) => {
|
|
62
|
+
if (!is_template(el)) {
|
|
63
|
+
warn("x-fragment can only be used on a 'template' tag.");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
anchor_block(el, el, { addScopeToNode, cleanup, initTree, mutateDom });
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { plugin as fragment };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=e=>e instanceof HTMLTemplateElement,o=e=>e.nodeType===Node.ELEMENT_NODE;function t({addScopeToNode:t,directive:n,initTree:r,mutateDom:l}){n("fragment",((n,{},{cleanup:d})=>{e(n)?function(t,n,{addScopeToNode:r,cleanup:l,initTree:d,mutateDom:a,scope:c={}}){if(t._r_block)return;document.body._r_block??=(()=>{const e=new MutationObserver((e=>{for(let o of e)for(let e of o.addedNodes)e._r_block?.update()}));return e.observe(document.body,{childList:!0,subtree:!0}),e})();let f=e(n)?[...n.content.cloneNode(!0).childNodes]:[n.cloneNode(!0)];a((()=>{for(let e of f)o(e)&&r(e,c,t),t.parentElement.insertBefore(e,t),o(e)&&d(e)})),t._r_block={template:n,update(){a((()=>{for(let e of f??[])t.parentElement.insertBefore(e,t)}))},delete(){t._r_block=null;for(let e of f??[])e.remove();f=null}},l((()=>t._r_block?.delete()))}(n,n,{addScopeToNode:t,cleanup:d,initTree:r,mutateDom:l}):console.warn("alpine-gear.js:","x-fragment can only be used on a 'template' tag.")}))}export{t as fragment};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const warn = (...args) => console.warn("alpine-gear.js:", ...args);
|
|
5
|
+
const is_template = el => el instanceof HTMLTemplateElement;
|
|
6
|
+
const is_element = el => el.nodeType === Node.ELEMENT_NODE;
|
|
7
|
+
|
|
8
|
+
function anchor_block(el, template, { addScopeToNode, cleanup, initTree, mutateDom, scope = {} }) {
|
|
9
|
+
if (el._r_block) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
initialize();
|
|
14
|
+
|
|
15
|
+
let nodes = is_template(template)
|
|
16
|
+
? [...template.content.cloneNode(true).childNodes]
|
|
17
|
+
: [template.cloneNode(true)];
|
|
18
|
+
|
|
19
|
+
mutateDom(() => {
|
|
20
|
+
for (let node of nodes) {
|
|
21
|
+
is_element(node) && addScopeToNode(node, scope, el);
|
|
22
|
+
el.parentElement.insertBefore(node, el);
|
|
23
|
+
is_element(node) && initTree(node);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
el._r_block = {
|
|
28
|
+
template,
|
|
29
|
+
update() {
|
|
30
|
+
mutateDom(() => {
|
|
31
|
+
for (let node of nodes ?? []) {
|
|
32
|
+
el.parentElement.insertBefore(node, el);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
delete() {
|
|
37
|
+
el._r_block = null;
|
|
38
|
+
for (let node of nodes ?? []) {
|
|
39
|
+
node.remove();
|
|
40
|
+
}
|
|
41
|
+
nodes = null;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
cleanup(() => el._r_block?.delete());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function initialize() {
|
|
49
|
+
document.body._r_block ??= (() => {
|
|
50
|
+
const observer = new MutationObserver(mutations => {
|
|
51
|
+
for (let mutation of mutations) {
|
|
52
|
+
for (let node of mutation.addedNodes) {
|
|
53
|
+
node._r_block?.update();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
59
|
+
return observer;
|
|
60
|
+
})();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function plugin({ addScopeToNode, directive, initTree, mutateDom }) {
|
|
64
|
+
directive("fragment", (el, {}, { cleanup }) => {
|
|
65
|
+
if (!is_template(el)) {
|
|
66
|
+
warn("x-fragment can only be used on a 'template' tag.");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
anchor_block(el, el, { addScopeToNode, cleanup, initTree, mutateDom });
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
document.addEventListener("alpine:init", () => { Alpine.plugin(plugin); });
|
|
75
|
+
|
|
76
|
+
})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(){"use strict";const e=e=>e instanceof HTMLTemplateElement,o=e=>e.nodeType===Node.ELEMENT_NODE;function t({addScopeToNode:t,directive:n,initTree:l,mutateDom:r}){n("fragment",((n,{},{cleanup:d})=>{e(n)?function(t,n,{addScopeToNode:l,cleanup:r,initTree:d,mutateDom:c,scope:a={}}){if(t._r_block)return;document.body._r_block??=(()=>{const e=new MutationObserver((e=>{for(let o of e)for(let e of o.addedNodes)e._r_block?.update()}));return e.observe(document.body,{childList:!0,subtree:!0}),e})();let i=e(n)?[...n.content.cloneNode(!0).childNodes]:[n.cloneNode(!0)];c((()=>{for(let e of i)o(e)&&l(e,a,t),t.parentElement.insertBefore(e,t),o(e)&&d(e)})),t._r_block={template:n,update(){c((()=>{for(let e of i??[])t.parentElement.insertBefore(e,t)}))},delete(){t._r_block=null;for(let e of i??[])e.remove();i=null}},r((()=>t._r_block?.delete()))}(n,n,{addScopeToNode:t,cleanup:d,initTree:l,mutateDom:r}):console.warn("alpine-gear.js:","x-fragment can only be used on a 'template' tag.")}))}document.addEventListener("alpine:init",(()=>{Alpine.plugin(t)}))}();
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ramstack/alpinegear-fragment",
|
|
3
|
+
"version": "1.0.0-preview.1",
|
|
4
|
+
"description": "@ramstack/alpinegear-fragment provides 'x-format' Alpine.js directive, allowing for fragment-like behavior similar to what's available in frameworks like 'Vue.js' or 'React', where multiple root elements can be grouped together.",
|
|
5
|
+
"author": "Rameel Burhan",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/rameel/ramstack.alpinegear.js.git",
|
|
10
|
+
"directory": "src/plugins/fragment"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"alpine.js",
|
|
14
|
+
"alpinejs"
|
|
15
|
+
],
|
|
16
|
+
"main": "alpinegear-fragment.js",
|
|
17
|
+
"module": "alpinegear-fragment.esm.js"
|
|
18
|
+
}
|