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 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
@@ -0,0 +1,5 @@
1
+ export * from "./src/lib/app";
2
+ export * from "./src/lib/dom";
3
+ export * from "./src/lib/event";
4
+ export * from "./src/lib/signal";
5
+ export * from "./src/lib/template";
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
+ }
@@ -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
+ }
@@ -0,0 +1,8 @@
1
+ export function emit(name, data) {
2
+ const event = new CustomEvent(name, { detail: data });
3
+ document.dispatchEvent(event);
4
+ }
5
+
6
+ export function output(name, callback) {
7
+ document.addEventListener(name, callback);
8
+ }
@@ -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
@@ -0,0 +1,5 @@
1
+ import './style.scss';
2
+ import { App } from './lib/app';
3
+ import routes from './routes';
4
+
5
+ App(routes);
@@ -0,0 +1,3 @@
1
+ <h1>Hello {{name}}.</h1>
2
+ <p>Count: {{count}}</p>
3
+ <button onclick="$this.setCount()">Click here</button>
@@ -0,0 +1,11 @@
1
+ import { Render, $this } from "../../lib/app";
2
+
3
+ export default function Home(params) {
4
+ Render('home', {
5
+ name: 'Victor',
6
+ count: 0,
7
+ setCount: () => {
8
+ $this.count += 1;
9
+ }
10
+ });
11
+ }
File without changes
package/src/routes.js ADDED
@@ -0,0 +1,5 @@
1
+ import Home from "./pages/home/home";
2
+
3
+ export default {
4
+ '/': (params) => Home(params)
5
+ }
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
+ }