vfront-lib 0.0.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 +79 -0
- package/index.html +13 -0
- package/index.js +5 -0
- package/package.json +39 -0
- package/public/vite.svg +1 -0
- package/src/lib/app.js +38 -0
- package/src/lib/dom.js +18 -0
- package/src/lib/event.js +8 -0
- package/src/lib/signal.js +36 -0
- package/src/lib/template.js +19 -0
- package/src/main.js +5 -0
- package/src/pages/home/home.html +3 -0
- package/src/pages/home/home.js +11 -0
- package/src/pages/home/home.scss +0 -0
- package/src/routes.js +5 -0
- package/src/style.scss +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# VFront
|
|
2
|
+
|
|
3
|
+
Simple SPA with Signals.
|
|
4
|
+
|
|
5
|
+
### Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i vfront
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
index.html
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<html lang="en">
|
|
15
|
+
<head>
|
|
16
|
+
<meta charset="UTF-8" />
|
|
17
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
18
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
19
|
+
<title>vfront</title>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<div id="app"></div>
|
|
23
|
+
<script type="module" src="/src/main.js"></script>
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Create component
|
|
29
|
+
|
|
30
|
+
Structure
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
├── src/
|
|
34
|
+
│ ├── pages/
|
|
35
|
+
│ │ ├── home/
|
|
36
|
+
│ │ │ ├── home.html
|
|
37
|
+
│ │ │ ├── home.scss
|
|
38
|
+
│ │ │ └── home.ts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
import { Render, $this } from "vfront";
|
|
43
|
+
|
|
44
|
+
export default function Home(params) {
|
|
45
|
+
// Render(htmlFileUniqueName, objectSignal)
|
|
46
|
+
Render('home', {
|
|
47
|
+
name: 'Steve',
|
|
48
|
+
count: 0,
|
|
49
|
+
setCount: () => {
|
|
50
|
+
$this.count += 1;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```html
|
|
57
|
+
<h1>Hello {{name}}.</h1>
|
|
58
|
+
<p>Count: {{count}}</p>
|
|
59
|
+
<button onclick="$this.setCount()">Click here</button>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Create route
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
import Home from "./pages/home/home";
|
|
66
|
+
|
|
67
|
+
export default {
|
|
68
|
+
'/': (params) => Home(params)
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Start app
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
import { App } from 'vfront';
|
|
76
|
+
import routes from './routes';
|
|
77
|
+
|
|
78
|
+
App(routes);
|
|
79
|
+
```
|
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>vfront</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.js"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vfront-lib",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"module": "index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
"import": "index.js"
|
|
9
|
+
},
|
|
10
|
+
"author": "devvime",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"spa",
|
|
14
|
+
"routes",
|
|
15
|
+
"signal",
|
|
16
|
+
"reactive-proxy"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/devvime/VFront.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/devvime/VFront/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/devvime/VFront#readme",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"dev": "vite",
|
|
28
|
+
"build": "vite build",
|
|
29
|
+
"preview": "vite preview"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"sass-embedded": "^1.97.1",
|
|
33
|
+
"vite": "^7.2.4"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"handlebars": "^4.7.8",
|
|
37
|
+
"navigo": "^8.11.1"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/public/vite.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
package/src/lib/app.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Navigo from "navigo";
|
|
2
|
+
import Handlebars from "handlebars";
|
|
3
|
+
import { Templates, Styles } from "./template";
|
|
4
|
+
import { Signal } from "./signal";
|
|
5
|
+
import { CreateElement } from "./dom";
|
|
6
|
+
|
|
7
|
+
const app = document.getElementById('app');
|
|
8
|
+
|
|
9
|
+
export let $this = null;
|
|
10
|
+
export let $app = null;
|
|
11
|
+
|
|
12
|
+
export const Router = new Navigo("/", { hash: true });
|
|
13
|
+
|
|
14
|
+
export function Render(name, data = {}) {
|
|
15
|
+
const element = Templates.find(t => t.path.includes(name))?.html;
|
|
16
|
+
const style = Styles.find(t => t.path.includes(name))?.style;
|
|
17
|
+
|
|
18
|
+
if (element === null || element === undefined) {
|
|
19
|
+
throw new Error(`Template ${name} is not found.`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const template = Handlebars.compile(element);
|
|
23
|
+
|
|
24
|
+
const tpl = (values => CreateElement(`<${name}><style>${style}</style>${template(values)}</${name}>`));
|
|
25
|
+
|
|
26
|
+
window.$this = Signal(data, (values) => {
|
|
27
|
+
app.replaceChildren(tpl(values));
|
|
28
|
+
window; $app = app;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
$this = window.$this;
|
|
32
|
+
$app = window.$app;
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function App(routes) {
|
|
37
|
+
Router.on(routes).resolve();
|
|
38
|
+
}
|
package/src/lib/dom.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function click(element, callback) {
|
|
2
|
+
const el = document.querySelector(`[${element}]`);
|
|
3
|
+
|
|
4
|
+
if (el === undefined && el === null) {
|
|
5
|
+
throw new Error(`Element ${element} is not found.`);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
el.addEventListener('click', () => {
|
|
9
|
+
callback()
|
|
10
|
+
click(element, callback);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function CreateElement(html) {
|
|
15
|
+
const template = document.createElement("template");
|
|
16
|
+
template.innerHTML = (html || "").trim();
|
|
17
|
+
return template.content.firstElementChild;
|
|
18
|
+
}
|
package/src/lib/event.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
let activeEffect = null;
|
|
2
|
+
|
|
3
|
+
export function signal(value) {
|
|
4
|
+
const effects = new Set();
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
if (activeEffect) effects.add(activeEffect);
|
|
9
|
+
return value;
|
|
10
|
+
},
|
|
11
|
+
set(v) {
|
|
12
|
+
value = v;
|
|
13
|
+
effects.forEach(fn => fn());
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function effect(fn) {
|
|
19
|
+
activeEffect = fn;
|
|
20
|
+
fn();
|
|
21
|
+
activeEffect = null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function Signal(obj, callback) {
|
|
25
|
+
const sig = signal(obj);
|
|
26
|
+
callback(obj);
|
|
27
|
+
|
|
28
|
+
return new Proxy(obj, {
|
|
29
|
+
set(target, prop, value) {
|
|
30
|
+
target[prop] = value;
|
|
31
|
+
callback(obj);
|
|
32
|
+
sig.set({ ...target });
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const Templates = Object.entries(
|
|
2
|
+
import.meta.glob('/src/**/*.html', {
|
|
3
|
+
as: 'raw',
|
|
4
|
+
eager: true
|
|
5
|
+
})
|
|
6
|
+
).map(([path, html]) => ({
|
|
7
|
+
path,
|
|
8
|
+
html
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
export const Styles = Object.entries(
|
|
12
|
+
import.meta.glob('/src/**/*.scss', {
|
|
13
|
+
as: 'raw',
|
|
14
|
+
eager: true
|
|
15
|
+
})
|
|
16
|
+
).map(([path, style]) => ({
|
|
17
|
+
path,
|
|
18
|
+
style
|
|
19
|
+
}));
|
package/src/main.js
ADDED
|
File without changes
|
package/src/routes.js
ADDED
package/src/style.scss
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
|
|
6
|
+
color-scheme: light dark;
|
|
7
|
+
color: rgba(255, 255, 255, 0.87);
|
|
8
|
+
background-color: #242424;
|
|
9
|
+
|
|
10
|
+
font-synthesis: none;
|
|
11
|
+
text-rendering: optimizeLegibility;
|
|
12
|
+
-webkit-font-smoothing: antialiased;
|
|
13
|
+
-moz-osx-font-smoothing: grayscale;
|
|
14
|
+
}
|