@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 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
+ }