create-snapp-app 1.0.1 → 2.0.0
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 +2 -1
- package/bin/create.js +16 -3
- package/package.json +24 -24
- package/template/{public/index.html → index.html} +13 -12
- package/template/package.json +9 -5
- package/template/public/.gitkeep +1 -0
- package/template/{public/style → style}/style.css +39 -39
- package/template/{public/views → views}/components/Links.jsx +12 -11
- package/template/{public/views → views}/index.jsx +28 -27
- package/template/public/src/index.js +0 -22
- package/template/public/src/snapp.js +0 -573
- /package/template/{public/assets → assets}/snapp.png +0 -0
package/README.md
CHANGED
package/bin/create.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
@@ -38,14 +38,27 @@ Example:
|
|
|
38
38
|
fs.mkdirSync(targetPath, { recursive: true });
|
|
39
39
|
fs.cpSync(templatePath, targetPath, { recursive: true });
|
|
40
40
|
|
|
41
|
+
// Replace {{APP_NAME}} in template files
|
|
42
|
+
const packageJsonPath = path.join(targetPath, "package.json");
|
|
43
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
44
|
+
let content = fs.readFileSync(packageJsonPath, "utf-8");
|
|
45
|
+
fs.writeFileSync(packageJsonPath, content.replace(/{{APP_NAME}}/g, appName));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const indexHtmlPath = path.join(targetPath, "index.html");
|
|
49
|
+
if (fs.existsSync(indexHtmlPath)) {
|
|
50
|
+
let content = fs.readFileSync(indexHtmlPath, "utf-8");
|
|
51
|
+
fs.writeFileSync(indexHtmlPath, content.replace(/{{APP_NAME}}/g, appName));
|
|
52
|
+
}
|
|
53
|
+
|
|
41
54
|
console.log(`
|
|
42
|
-
Successfully created Snapp app!
|
|
55
|
+
Successfully created Snapp app: ${appName}!
|
|
43
56
|
|
|
44
57
|
Next:
|
|
45
58
|
cd ${appName}
|
|
46
59
|
|
|
47
60
|
npm install
|
|
48
|
-
npm run
|
|
61
|
+
npm run dev
|
|
49
62
|
`);
|
|
50
63
|
} catch (error) {
|
|
51
64
|
console.error("❌ Some error came up:", error.message);
|
package/package.json
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "create-snapp-app",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Create Snapp apps with zero configuration",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"create-snapp-app": "./bin/create.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"bin",
|
|
11
|
-
"template"
|
|
12
|
-
],
|
|
13
|
-
"keywords": [
|
|
14
|
-
"snapp",
|
|
15
|
-
"create-snapp-app",
|
|
16
|
-
"cli",
|
|
17
|
-
"boilerplate"
|
|
18
|
-
],
|
|
19
|
-
"author": "kigemmanuel",
|
|
20
|
-
"license": "MIT",
|
|
21
|
-
"engines": {
|
|
22
|
-
"node": ">=14.0.0"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "create-snapp-app",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Create Snapp apps with zero configuration",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-snapp-app": "./bin/create.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"template"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"snapp",
|
|
15
|
+
"create-snapp-app",
|
|
16
|
+
"cli",
|
|
17
|
+
"boilerplate"
|
|
18
|
+
],
|
|
19
|
+
"author": "kigemmanuel",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=14.0.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
</
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>{{APP_NAME}}</title>
|
|
6
|
+
<link rel="stylesheet" href="style/style.css">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="public/index.js"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/template/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "{{APP_NAME}}",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
-
"description": "My
|
|
4
|
+
"description": "My Snapp app",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"scripts": {
|
|
6
|
-
"
|
|
7
|
-
"
|
|
7
|
+
"dev": "npx snapp build -W",
|
|
8
|
+
"build": "npx snapp build -M"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@snappjs/core": "^3.0.0"
|
|
8
12
|
},
|
|
9
13
|
"devDependencies": {
|
|
10
|
-
"snapp-kit": "^3.
|
|
14
|
+
"snapp-kit": "^3.1.0"
|
|
11
15
|
}
|
|
12
16
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
.button {
|
|
3
|
-
background-color: #4a90e2;
|
|
4
|
-
color: #fff;
|
|
5
|
-
border: none;
|
|
6
|
-
border-radius: 8px;
|
|
7
|
-
padding: 10px 18px;
|
|
8
|
-
font-size: 16px;
|
|
9
|
-
cursor: pointer;
|
|
10
|
-
transition: background 0.2s, transform 0.2s;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.button:hover {
|
|
14
|
-
background-color: #357abd;
|
|
15
|
-
transform: translateY(-2px);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
.center-div {
|
|
19
|
-
position: absolute;
|
|
20
|
-
display: grid;
|
|
21
|
-
place-items: center;
|
|
22
|
-
top: 50%;
|
|
23
|
-
left: 50%;
|
|
24
|
-
width: 100%;
|
|
25
|
-
transform: translate(-50%, -50%);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.a-href {
|
|
29
|
-
color: #4a90e2;
|
|
30
|
-
text-decoration: none;
|
|
31
|
-
font-weight: bold;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.link-div {
|
|
35
|
-
margin: 10px 0;
|
|
36
|
-
display: flex;
|
|
37
|
-
flex-direction: column;
|
|
38
|
-
text-align: center;
|
|
39
|
-
}
|
|
1
|
+
|
|
2
|
+
.button {
|
|
3
|
+
background-color: #4a90e2;
|
|
4
|
+
color: #fff;
|
|
5
|
+
border: none;
|
|
6
|
+
border-radius: 8px;
|
|
7
|
+
padding: 10px 18px;
|
|
8
|
+
font-size: 16px;
|
|
9
|
+
cursor: pointer;
|
|
10
|
+
transition: background 0.2s, transform 0.2s;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.button:hover {
|
|
14
|
+
background-color: #357abd;
|
|
15
|
+
transform: translateY(-2px);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.center-div {
|
|
19
|
+
position: absolute;
|
|
20
|
+
display: grid;
|
|
21
|
+
place-items: center;
|
|
22
|
+
top: 50%;
|
|
23
|
+
left: 50%;
|
|
24
|
+
width: 100%;
|
|
25
|
+
transform: translate(-50%, -50%);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.a-href {
|
|
29
|
+
color: #4a90e2;
|
|
30
|
+
text-decoration: none;
|
|
31
|
+
font-weight: bold;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.link-div {
|
|
35
|
+
margin: 10px 0;
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
text-align: center;
|
|
39
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import snapp from "@snappjs/core"
|
|
2
|
+
|
|
3
|
+
const Links = () => {
|
|
4
|
+
|
|
5
|
+
return <div className="link-div">
|
|
6
|
+
<a className="a-href" target="_blank" href="https://github.com/SnappJS/snapp">Learn Snapp</a>
|
|
7
|
+
<span className="a-href" style={{color: '#0C2340'}}>Please star and follow</span>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default Links
|
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
import snapp from "@snappjs/core"
|
|
2
|
+
import Links from "./components/Links"
|
|
3
|
+
|
|
4
|
+
const App = () => {
|
|
5
|
+
|
|
6
|
+
const count = snapp.dynamic(0)
|
|
7
|
+
|
|
8
|
+
return <div className="center-div">
|
|
9
|
+
|
|
10
|
+
<img style={{width: "auto", height: "200px"}} src="assets/snapp.png" alt="snapp" />
|
|
11
|
+
<h2>Welcome to Snapp {() => count.value}</h2>
|
|
12
|
+
|
|
13
|
+
<button
|
|
14
|
+
className="button"
|
|
15
|
+
onClick={() => count.update(count.value + 1)}
|
|
16
|
+
>
|
|
17
|
+
Click To Count
|
|
18
|
+
</button>
|
|
19
|
+
|
|
20
|
+
<Links />
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const app = snapp.select("#app");
|
|
26
|
+
snapp.render(app, App(), null, () => {
|
|
27
|
+
console.log("Snapp rendered successfully")
|
|
28
|
+
})
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import snapp from "./snapp.js";
|
|
2
|
-
|
|
3
|
-
// public/views/components/Links.jsx
|
|
4
|
-
var Links = () => {
|
|
5
|
-
return /* @__PURE__ */ snapp.create("div", { className: "link-div" }, /* @__PURE__ */ snapp.create("a", { className: "a-href", target: "_blank", href: "https://github.com/kigemmanuel/Snapp" }, "Learn Snapp"), /* @__PURE__ */ snapp.create("span", { className: "a-href", style: { color: "#0C2340" } }, "Please star and follow"));
|
|
6
|
-
};
|
|
7
|
-
var Links_default = Links;
|
|
8
|
-
|
|
9
|
-
// public/views/index.jsx
|
|
10
|
-
var App = () => {
|
|
11
|
-
const count = snapp.dynamic(0);
|
|
12
|
-
return /* @__PURE__ */ snapp.create("div", { className: "center-div" }, /* @__PURE__ */ snapp.create("img", { style: { width: "auto", height: "200px" }, src: "assets/snapp.png", alt: "snapp" }), /* @__PURE__ */ snapp.create("h2", null, "Welcome to snapp ", () => count.value), /* @__PURE__ */ snapp.create(
|
|
13
|
-
"button",
|
|
14
|
-
{
|
|
15
|
-
className: "button",
|
|
16
|
-
onClick: () => count.update(count.value + 1)
|
|
17
|
-
},
|
|
18
|
-
"Click To Count"
|
|
19
|
-
), /* @__PURE__ */ snapp.create(Links_default, null));
|
|
20
|
-
};
|
|
21
|
-
var SnappBody = document.querySelector("#snapp-body");
|
|
22
|
-
snapp.render(SnappBody, App());
|
|
@@ -1,573 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/*
|
|
3
|
-
* Snapp Framework v2.1.1
|
|
4
|
-
* A lightweight JSX-like framework for vanilla JavaScript
|
|
5
|
-
*
|
|
6
|
-
* @version 2.1.1
|
|
7
|
-
* @license MIT
|
|
8
|
-
* @repository https://github.com/kigemmanuel/Snapp
|
|
9
|
-
*
|
|
10
|
-
* Features:
|
|
11
|
-
* - JSX-like syntax compilation
|
|
12
|
-
* - Component-based architecture
|
|
13
|
-
* - Zero dependencies
|
|
14
|
-
* - Lightweight and fast
|
|
15
|
-
;*l
|
|
16
|
-
- Dynamic state
|
|
17
|
-
*
|
|
18
|
-
* Built with ❤️ for modern web development
|
|
19
|
-
*
|
|
20
|
-
* Copyright (c) 2025 kigemmanuel
|
|
21
|
-
* Released under the MIT License
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
const snapp = (() => {
|
|
25
|
-
|
|
26
|
-
var dataId = 0;
|
|
27
|
-
var dynamicId = 1;
|
|
28
|
-
|
|
29
|
-
var DOMReady = false;
|
|
30
|
-
var track_dynamic = null;
|
|
31
|
-
|
|
32
|
-
const dynamicData = {};
|
|
33
|
-
const dynamicDependencies = new Map();
|
|
34
|
-
|
|
35
|
-
const eventListener = {};
|
|
36
|
-
const elementEvent = {};
|
|
37
|
-
|
|
38
|
-
const eventMap = {
|
|
39
|
-
'onmouseenter': 'onmouseover',
|
|
40
|
-
'onmouseleave': 'onmouseout',
|
|
41
|
-
'ondoubleclick': 'ondblclick'
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const SVG_ELEMENTS = new Set([
|
|
45
|
-
'svg', 'circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect',
|
|
46
|
-
'text', 'textPath', 'tspan', 'defs', 'g', 'marker', 'mask', 'pattern',
|
|
47
|
-
'switch', 'symbol', 'linearGradient', 'radialGradient', 'stop', 'filter',
|
|
48
|
-
'feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite',
|
|
49
|
-
'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight',
|
|
50
|
-
'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR',
|
|
51
|
-
'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology',
|
|
52
|
-
'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile',
|
|
53
|
-
'feTurbulence', 'image', 'use', 'foreignObject', 'animate', 'animateMotion',
|
|
54
|
-
'animateTransform', 'mpath', 'set', 'clipPath', 'desc', 'metadata', 'view'
|
|
55
|
-
]);
|
|
56
|
-
|
|
57
|
-
const create = (element, props, ...children) => {
|
|
58
|
-
const flatChildren = flattenChildren(children);
|
|
59
|
-
if (element != "<>" && typeof element === "string")
|
|
60
|
-
return createElement(element, props, flatChildren);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (typeof element === 'function')
|
|
64
|
-
return createComponent(element, props, flatChildren);
|
|
65
|
-
|
|
66
|
-
if (element === "<>")
|
|
67
|
-
return createFragment(flatChildren);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const render = (body, App, type, callBack) => {
|
|
71
|
-
|
|
72
|
-
if (!document.contains(body)) {
|
|
73
|
-
console.error("ERROR: Rending to a non existing/removed element", body)
|
|
74
|
-
if (typeof callBack === "function") return callBack(false)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (typeof App === 'string' || typeof App === 'number' || App instanceof Element || App instanceof DocumentFragment) {
|
|
78
|
-
DOMReady = false;
|
|
79
|
-
|
|
80
|
-
switch (true) {
|
|
81
|
-
case (type === "before"):
|
|
82
|
-
body.before(App);
|
|
83
|
-
break;
|
|
84
|
-
|
|
85
|
-
case (type === "prepend"):
|
|
86
|
-
body.prepend(App);
|
|
87
|
-
break;
|
|
88
|
-
|
|
89
|
-
case (type === "replace"):
|
|
90
|
-
body.replaceWith(App);
|
|
91
|
-
break;
|
|
92
|
-
|
|
93
|
-
case (type === "append"):
|
|
94
|
-
body.append(App);
|
|
95
|
-
break;
|
|
96
|
-
|
|
97
|
-
case (type === "after"):
|
|
98
|
-
body.after(App);
|
|
99
|
-
break;
|
|
100
|
-
|
|
101
|
-
default:
|
|
102
|
-
body.replaceChildren(App);
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
DOMReady = true;
|
|
107
|
-
document.dispatchEvent(new Event("DOM"))
|
|
108
|
-
if (typeof callBack === "function") return callBack(true);
|
|
109
|
-
|
|
110
|
-
} else {
|
|
111
|
-
console.error("Failed to render! ", typeof App, App)
|
|
112
|
-
if (typeof callBack === "function") return callBack(false)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const remove = (items) => {
|
|
117
|
-
|
|
118
|
-
items = (Array.isArray(items)) ? items : [items];
|
|
119
|
-
|
|
120
|
-
items.forEach(item => {
|
|
121
|
-
|
|
122
|
-
if (item instanceof Element) {
|
|
123
|
-
item.remove()
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
})
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const on = (event, callBack) => {
|
|
130
|
-
|
|
131
|
-
if (typeof event === "string" && event.toUpperCase() === "DOM") {
|
|
132
|
-
if (DOMReady === true) {
|
|
133
|
-
callBack()
|
|
134
|
-
} else {
|
|
135
|
-
document.addEventListener(event.toUpperCase(), callBack, { once: true })
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const select = (name) => {
|
|
142
|
-
if (typeof name === 'string') {
|
|
143
|
-
const element = document.querySelector(name);
|
|
144
|
-
if (!element) {
|
|
145
|
-
console.error(`Element with "${name}" not found`)
|
|
146
|
-
return null
|
|
147
|
-
}
|
|
148
|
-
return element
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (Array.isArray(name)) {
|
|
152
|
-
return name.map(ele => {
|
|
153
|
-
const element = document.querySelector(ele)
|
|
154
|
-
if (!element) {
|
|
155
|
-
console.error(`Element with "${ele}" not found`);
|
|
156
|
-
return null
|
|
157
|
-
}
|
|
158
|
-
return element
|
|
159
|
-
})
|
|
160
|
-
}
|
|
161
|
-
console.error("Invalid selector!")
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const selectAll = (name) => {
|
|
166
|
-
if (typeof name === 'string') {
|
|
167
|
-
const element = document.querySelectorAll(name)
|
|
168
|
-
|
|
169
|
-
if (element.length === 0) {
|
|
170
|
-
console.error(`Element with "${name}" not found`)
|
|
171
|
-
return null
|
|
172
|
-
}
|
|
173
|
-
return element
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (Array.isArray(name)) {
|
|
177
|
-
return name.map(ele => {
|
|
178
|
-
const element = document.querySelectorAll(ele)
|
|
179
|
-
|
|
180
|
-
if (element.length === 0) {
|
|
181
|
-
console.error(`Element with "${ele}" not found`)
|
|
182
|
-
return null
|
|
183
|
-
}
|
|
184
|
-
return element;
|
|
185
|
-
})
|
|
186
|
-
}
|
|
187
|
-
console.error("Invalid selector!")
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const createElement = (element, props, children) => {
|
|
192
|
-
const ele = SVG_ELEMENTS.has(element)
|
|
193
|
-
? document.createElementNS("http://www.w3.org/2000/svg", element)
|
|
194
|
-
: document.createElement(element);
|
|
195
|
-
|
|
196
|
-
dataId++
|
|
197
|
-
|
|
198
|
-
if (props) {
|
|
199
|
-
for (let [key, value] of Object.entries(props)) {
|
|
200
|
-
|
|
201
|
-
if (value == null || value === false) continue
|
|
202
|
-
|
|
203
|
-
if (key === "className") key = "class";
|
|
204
|
-
if (key === "htmlFor") key = "for";
|
|
205
|
-
|
|
206
|
-
if (value === true || value === "") {
|
|
207
|
-
ele.setAttribute(key, "")
|
|
208
|
-
continue
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (key === 'style') {
|
|
212
|
-
if (typeof value === 'object') {
|
|
213
|
-
for (const [property, style] of Object.entries(value)) {
|
|
214
|
-
track_dynamic = new Set()
|
|
215
|
-
const mainStyle = (typeof style === "function") ? style() : style;
|
|
216
|
-
const newDynamicId = [...track_dynamic]
|
|
217
|
-
track_dynamic = null;
|
|
218
|
-
|
|
219
|
-
if (newDynamicId.length > 0) {
|
|
220
|
-
const subscribeData = {
|
|
221
|
-
type: "style",
|
|
222
|
-
prop: property,
|
|
223
|
-
temp: style,
|
|
224
|
-
subscribe: newDynamicId
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
ele.setAttribute("snapp-dynamic", dataId);
|
|
228
|
-
subscribeDynamic(newDynamicId, subscribeData, ele)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (property.includes('-')) {
|
|
232
|
-
ele.style.setProperty(property, mainStyle);
|
|
233
|
-
} else {
|
|
234
|
-
ele.style[property] = mainStyle;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
console.warn(`Invalid style for ${element} `, value)
|
|
240
|
-
continue
|
|
241
|
-
}
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (key.startsWith("on") && key !== "on" && typeof value === "function") {
|
|
246
|
-
let lowerCaseKey = key.toLowerCase();
|
|
247
|
-
lowerCaseKey = eventMap[lowerCaseKey] || lowerCaseKey;
|
|
248
|
-
|
|
249
|
-
if (lowerCaseKey in ele) {
|
|
250
|
-
const eventType = lowerCaseKey.slice(2);
|
|
251
|
-
ele.setAttribute("snapp-data", dataId);
|
|
252
|
-
addEventListener(eventType, value, dataId);
|
|
253
|
-
ele.setAttribute("snapp-e-" + eventType, "true");
|
|
254
|
-
} else {
|
|
255
|
-
console.warn(`Event "${lowerCaseKey}", Do not exist for `, ele)
|
|
256
|
-
}
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (typeof value === "function" && !key.startsWith("on")) {
|
|
261
|
-
track_dynamic = new Set()
|
|
262
|
-
ele.setAttribute(key, value())
|
|
263
|
-
const newDynamicId = [...track_dynamic]
|
|
264
|
-
track_dynamic = null;
|
|
265
|
-
|
|
266
|
-
if (newDynamicId.length > 0) {
|
|
267
|
-
const subscribeData = {
|
|
268
|
-
type: "attr",
|
|
269
|
-
attr: key,
|
|
270
|
-
temp: value,
|
|
271
|
-
subscribe: newDynamicId
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
ele.setAttribute("snapp-dynamic", dataId);
|
|
275
|
-
subscribeDynamic(newDynamicId, subscribeData, ele)
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Default
|
|
282
|
-
ele.setAttribute(key, value);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
children.forEach(node => {
|
|
287
|
-
if (typeof node === 'string' || typeof node === 'number' || node instanceof Element || node instanceof DocumentFragment) {
|
|
288
|
-
ele.append(node);
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (typeof node === 'function') {
|
|
293
|
-
track_dynamic = new Set()
|
|
294
|
-
const textNode = document.createTextNode(node())
|
|
295
|
-
const newDynamicId = [...track_dynamic]
|
|
296
|
-
track_dynamic = null;
|
|
297
|
-
|
|
298
|
-
if (newDynamicId.length > 0) {
|
|
299
|
-
const subscribeData = {
|
|
300
|
-
type: "node",
|
|
301
|
-
node: textNode,
|
|
302
|
-
temp: node,
|
|
303
|
-
subscribe: newDynamicId
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
ele.setAttribute("snapp-dynamic", dataId);
|
|
307
|
-
subscribeDynamic(newDynamicId, subscribeData, ele)
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
ele.append(textNode)
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
return ele;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const createFragment = (children) => {
|
|
318
|
-
const frag = document.createDocumentFragment();
|
|
319
|
-
children.forEach(node => {
|
|
320
|
-
if (typeof node === 'string' || typeof node === 'number' || node instanceof Element || node instanceof DocumentFragment) {
|
|
321
|
-
frag.append(node);
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
return frag;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const createComponent = (element, props = {}, children) => {
|
|
329
|
-
const totalProps = { ...props, children };
|
|
330
|
-
const comp = element(totalProps);
|
|
331
|
-
|
|
332
|
-
return comp;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const flattenChildren = (children) => {
|
|
336
|
-
const final = []
|
|
337
|
-
|
|
338
|
-
for (const child of children) {
|
|
339
|
-
if (Array.isArray(child)) {
|
|
340
|
-
final.push(...flattenChildren(child))
|
|
341
|
-
} else if (child !== null && child !== undefined && child !== '' && child !== false) {
|
|
342
|
-
final.push(child)
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return final
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const applystyle = (element, styles) => {
|
|
351
|
-
element = (Array.isArray(element)) ? element : [element];
|
|
352
|
-
|
|
353
|
-
element.forEach(ele => {
|
|
354
|
-
|
|
355
|
-
if (!(ele instanceof Element)) {
|
|
356
|
-
console.error(`Error! can not apply style to "${element}", selelct a valid element`)
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (typeof styles === "object") {
|
|
361
|
-
for (const [property, style] of Object.entries(styles)) {
|
|
362
|
-
if (property.includes('-')) {
|
|
363
|
-
ele.style.setProperty(property, style);
|
|
364
|
-
} else {
|
|
365
|
-
ele.style[property] = style;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
})
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
const removestyle = (element, styles) => {
|
|
373
|
-
element = (Array.isArray(element)) ? element : [element];
|
|
374
|
-
|
|
375
|
-
element.forEach(ele => {
|
|
376
|
-
if (!(ele instanceof Element)) {
|
|
377
|
-
console.error(`Error! can not remove style from "${element}", selelct a valid element`)
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
if (styles === true) return ele.removeAttribute("style")
|
|
382
|
-
if (typeof styles === "object") {
|
|
383
|
-
for (const [property, style] of Object.entries(styles)) {
|
|
384
|
-
if (property.includes('-')) {
|
|
385
|
-
ele.style.removeProperty(property);
|
|
386
|
-
} else {
|
|
387
|
-
ele.style[property] = "";
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
})
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const addEventListener = (eventType, event, elementId) => {
|
|
396
|
-
|
|
397
|
-
const eventTemplate = (element) => {
|
|
398
|
-
const target = element.target;
|
|
399
|
-
|
|
400
|
-
if (!target || target.nodeType !== 1) {
|
|
401
|
-
console.log('Target is not an element, skipping...');
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const elWithAttr = target.closest(`[snapp-e-${eventType}]`);
|
|
406
|
-
if (!elWithAttr) return;
|
|
407
|
-
const elementDataId = elWithAttr.getAttribute("snapp-data");
|
|
408
|
-
elementEvent[eventType]?.[elementDataId](element)
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (!(eventType in eventListener)) {
|
|
412
|
-
elementEvent[eventType] = {}
|
|
413
|
-
|
|
414
|
-
eventListener[eventType] = eventTemplate;
|
|
415
|
-
document.addEventListener(eventType, eventListener[eventType])
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
elementEvent[eventType][elementId] = event;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const dynamic = (initialtValue = "") => {
|
|
423
|
-
const id = `dynamic-${dynamicId++}`;
|
|
424
|
-
dynamicData[id] = {
|
|
425
|
-
value: initialtValue,
|
|
426
|
-
subscribe: new Map()
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
const update = (newValue) => {
|
|
430
|
-
if (dynamicData[id].value !== newValue) {
|
|
431
|
-
dynamicData[id].value = newValue;
|
|
432
|
-
for (const [element, items] of dynamicData[id].subscribe) {
|
|
433
|
-
updateDynamicValue(element, items)
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
return {
|
|
439
|
-
get value() {
|
|
440
|
-
if (track_dynamic) {
|
|
441
|
-
track_dynamic.add(id)
|
|
442
|
-
}
|
|
443
|
-
return dynamicData[id].value
|
|
444
|
-
},
|
|
445
|
-
update,
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
const updateDynamicValue = (element, items) => {
|
|
450
|
-
items.forEach(item => {
|
|
451
|
-
const dynamic = dynamicDependencies.get(element)?.[item];
|
|
452
|
-
if (dynamic) {
|
|
453
|
-
const previousDynamicId = new Set(dynamic.subscribe);
|
|
454
|
-
track_dynamic = new Set();
|
|
455
|
-
const newTemp = dynamic.temp();
|
|
456
|
-
const newTrack_dynamic = [...track_dynamic]
|
|
457
|
-
track_dynamic = null;
|
|
458
|
-
|
|
459
|
-
if (dynamic.type === "node") {
|
|
460
|
-
dynamic.node.nodeValue = newTemp;
|
|
461
|
-
} else if (dynamic.type === "attr") {
|
|
462
|
-
element.setAttribute(dynamic.attr, newTemp)
|
|
463
|
-
} else if (dynamic.type === "style") {
|
|
464
|
-
if (newTemp.includes('-')) {
|
|
465
|
-
element.style.setProperty(dynamic.prop, newTemp);
|
|
466
|
-
} else {
|
|
467
|
-
element.style[dynamic.prop] = newTemp;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
newTrack_dynamic.forEach(dynamicId => {
|
|
472
|
-
if (previousDynamicId.has(dynamicId)) return
|
|
473
|
-
|
|
474
|
-
if (!dynamicData[dynamicId]["subscribe"].has(element)) {
|
|
475
|
-
dynamicData[dynamicId]["subscribe"].set(element, [])
|
|
476
|
-
}
|
|
477
|
-
dynamicData[dynamicId]["subscribe"].get(element).push(item)
|
|
478
|
-
dynamicDependencies.get(element)[item].subscribe.push(dynamicId)
|
|
479
|
-
})
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
})
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
const subscribeDynamic = (dynamicId, subscribeData, element) => {
|
|
486
|
-
if (!dynamicDependencies.has(element)) {
|
|
487
|
-
dynamicDependencies.set(element, [])
|
|
488
|
-
}
|
|
489
|
-
dynamicDependencies.get(element).push(subscribeData)
|
|
490
|
-
const subscribeIndex = dynamicDependencies.get(element).length - 1;
|
|
491
|
-
|
|
492
|
-
dynamicId.forEach(id => {
|
|
493
|
-
if (!dynamicData[id]) return
|
|
494
|
-
|
|
495
|
-
if (!dynamicData[id]["subscribe"].has(element)) {
|
|
496
|
-
dynamicData[id]["subscribe"].set(element, [])
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
dynamicData[id]["subscribe"].get(element).push(subscribeIndex)
|
|
500
|
-
})
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
const observer = new MutationObserver((mutations) => {
|
|
504
|
-
mutations.forEach(element => {
|
|
505
|
-
element.removedNodes.forEach(node => {
|
|
506
|
-
if (node instanceof Element) {
|
|
507
|
-
const elementDataId = node.getAttribute("snapp-data");
|
|
508
|
-
if (elementDataId) {
|
|
509
|
-
for (const attrName of node.getAttributeNames()) {
|
|
510
|
-
if (attrName.startsWith('snapp-e-')) {
|
|
511
|
-
const eventType = attrName.replace("snapp-e-", "");
|
|
512
|
-
|
|
513
|
-
if (elementEvent[eventType]?.[elementDataId]) {
|
|
514
|
-
delete elementEvent[eventType]?.[elementDataId]
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (Object.keys(elementEvent[eventType]).length === 0) {
|
|
518
|
-
document.removeEventListener(eventType, eventListener[eventType])
|
|
519
|
-
delete eventListener[eventType]
|
|
520
|
-
delete elementEvent[eventType]
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
if (node.getAttribute("snapp-dynamic")) {
|
|
527
|
-
dynamicDependencies.delete(node);
|
|
528
|
-
callCleardynamicElement()
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
}
|
|
532
|
-
})
|
|
533
|
-
});
|
|
534
|
-
})
|
|
535
|
-
observer.observe(document, {
|
|
536
|
-
childList: true,
|
|
537
|
-
subtree: true
|
|
538
|
-
})
|
|
539
|
-
|
|
540
|
-
let timeOut = null;
|
|
541
|
-
let callCount = 0;
|
|
542
|
-
const callCleardynamicElement = (delay = 15000, threshold = 30) => {
|
|
543
|
-
callCount++
|
|
544
|
-
clearTimeout(timeOut);
|
|
545
|
-
|
|
546
|
-
if (callCount >= threshold) {
|
|
547
|
-
cleardynamicElement()
|
|
548
|
-
callCount = 0;
|
|
549
|
-
} else {
|
|
550
|
-
timeOut = setTimeout(() => {
|
|
551
|
-
cleardynamicElement()
|
|
552
|
-
callCount = 0;
|
|
553
|
-
}, delay)
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
const cleardynamicElement = () => {
|
|
558
|
-
for (const [dynamicId, items] of Object.entries(dynamicData)) {
|
|
559
|
-
for (const [element, data] of items.subscribe) {
|
|
560
|
-
if (!element.isConnected) {
|
|
561
|
-
dynamicData[dynamicId].subscribe.delete(element)
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
return {
|
|
568
|
-
create, render, on, select, selectAll, applystyle, removestyle, remove, dynamic
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
})()
|
|
572
|
-
|
|
573
|
-
export default snapp;
|
|
File without changes
|