uigar 0.3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Vugar Safarzada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # uigar
2
+
3
+ **uigar** is a simple and declarative JavaScript framework for building user interfaces, inspired by component-based architecture. It allows you to build a web application's UI using a declarative, function-based syntax.
4
+
5
+ ## Get Started
6
+
7
+ The quickest way to start building with `uigar` is by creating a new project using our command-line tool.
8
+
9
+ ```bash
10
+ npx uigar create my-uigar-app
11
+ ```
12
+
13
+ This command will set up a new directory named `my-uigar-app` with a ready-to-go `uigar` project.
14
+
15
+ Once the project is created, navigate into it and start the development server:
16
+
17
+ ```bash
18
+ cd my-uigar-app
19
+ npm install
20
+ npm run start
21
+ ```
22
+
23
+ ## Usage as a Library
24
+
25
+ You can also install `uigar` into an existing project.
26
+
27
+ ### Installation
28
+
29
+ ```bash
30
+ npm install uigar
31
+ ```
32
+ or
33
+ ```bash
34
+ yarn add uigar
35
+ ```
36
+
37
+ ### Basic Component Example
38
+
39
+ Here is a simple example of a `uigar` component that demonstrates state management:
40
+
41
+ ```typescript
42
+ import { div, h1, p, button, Component, render } from 'uigar';
43
+
44
+ interface CounterState {
45
+ count: number;
46
+ }
47
+
48
+ class CounterComponent extends Component<CounterState> {
49
+ constructor() {
50
+ super({ count: 0 });
51
+ }
52
+
53
+ render() {
54
+ return div(
55
+ h1('Counter App'),
56
+ p(`Count: ${this.state.count}`),
57
+ button('Increment')
58
+ .onClick(() => this.setState({ count: this.state.count + 1 }))
59
+ .style({ padding: '10px 20px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' })
60
+ );
61
+ }
62
+ }
63
+
64
+ // To render this component to the DOM:
65
+ render('#app', new CounterComponent());
66
+ ```
67
+
68
+ ## Further Documentation
69
+
70
+ For more detailed API references, advanced usage patterns, and tutorials, please visit our [documentation site](./docs/introduction.md).
71
+
72
+ ## Example Application
73
+
74
+ This repository includes a full example application in the `/example` directory. It's a great way to explore various features of `uigar`.
75
+
76
+ To run the example locally:
77
+
78
+ 1. Navigate to the `example` directory:
79
+ ```bash
80
+ cd example
81
+ ```
82
+ 2. Install dependencies:
83
+ ```bash
84
+ npm install
85
+ ```
86
+ 3. Start the development server:
87
+ ```bash
88
+ npm run start
89
+ ```
90
+
91
+ # uigar
package/cli.js ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const args = process.argv.slice(2);
7
+ const command = args[0];
8
+ const projectName = args[1];
9
+
10
+ if (command === "create") {
11
+ if (!projectName) {
12
+ console.error("Please provide a project name.");
13
+ process.exit(1);
14
+ }
15
+
16
+ const currentDir = process.cwd();
17
+ const projectDir = path.resolve(currentDir, projectName);
18
+ fs.mkdirSync(projectDir, { recursive: true });
19
+
20
+ const templateDir = path.resolve(__dirname, "template");
21
+ fs.cpSync(templateDir, projectDir, { recursive: true });
22
+
23
+ const projectPackageJsonPath = path.join(projectDir, "package.json");
24
+ let projectPackageJson = JSON.parse(
25
+ fs.readFileSync(projectPackageJsonPath, "utf8")
26
+ );
27
+
28
+ projectPackageJson.name = projectName;
29
+
30
+ fs.writeFileSync(
31
+ projectPackageJsonPath,
32
+ JSON.stringify(projectPackageJson, null, 2)
33
+ );
34
+
35
+ console.log("Success! Your new uigar project has been created.");
36
+ console.log(`To get started, run the following commands:`);
37
+ console.log(`cd ${projectName}`);
38
+ console.log(`npm install`);
39
+ console.log(`npm run start`);
40
+ } else {
41
+ console.log("Unknown command. Did you mean:");
42
+ console.log("npx uigar create <project-name>");
43
+ }
@@ -0,0 +1,33 @@
1
+ declare class DomElement {
2
+ element: HTMLElement;
3
+ constructor(tagName: string, ...children: (DomElement | HTMLElement | string)[]);
4
+ private addChildren;
5
+ style(styles: Partial<CSSStyleDeclaration>): this;
6
+ settings(attributes: {
7
+ [key: string]: string;
8
+ }): this;
9
+ onClick(handler: (e: MouseEvent) => void): this;
10
+ on(event: string, handler: (e: Event) => void): this;
11
+ render(): HTMLElement;
12
+ }
13
+ type Child = DomElement | HTMLElement | string;
14
+ export declare const div: (...children: Child[]) => DomElement;
15
+ export declare const h1: (...children: Child[]) => DomElement;
16
+ export declare const p: (...children: Child[]) => DomElement;
17
+ export declare const a: (...children: Child[]) => DomElement;
18
+ export declare const span: (...children: Child[]) => DomElement;
19
+ export declare const img: () => DomElement;
20
+ export declare const input: () => DomElement;
21
+ export declare const button: (...children: Child[]) => DomElement;
22
+ export declare abstract class Component<S extends object = {}> {
23
+ protected state: S;
24
+ private _mountedElement;
25
+ private _parentSelector;
26
+ constructor(initialState?: S);
27
+ abstract render(): DomElement;
28
+ protected setState(newState: Partial<S>): void;
29
+ private update;
30
+ _mount(selector: string): HTMLElement;
31
+ }
32
+ export declare function render(selector: string, component: Component): void;
33
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.render = exports.Component = exports.button = exports.input = exports.img = exports.span = exports.a = exports.p = exports.h1 = exports.div = void 0;
4
+ // A simple DOM element wrapper
5
+ class DomElement {
6
+ constructor(tagName, ...children) {
7
+ this.element = document.createElement(tagName);
8
+ this.addChildren(children);
9
+ }
10
+ addChildren(children) {
11
+ for (const child of children) {
12
+ if (typeof child === 'string') {
13
+ this.element.appendChild(document.createTextNode(child));
14
+ }
15
+ else if (child instanceof DomElement) {
16
+ this.element.appendChild(child.render());
17
+ }
18
+ else if (child instanceof HTMLElement) {
19
+ this.element.appendChild(child);
20
+ }
21
+ }
22
+ }
23
+ style(styles) {
24
+ for (const key in styles) {
25
+ if (Object.prototype.hasOwnProperty.call(styles, key)) {
26
+ const styleKey = key;
27
+ this.element.style[styleKey] = styles[styleKey];
28
+ }
29
+ }
30
+ return this;
31
+ }
32
+ settings(attributes) {
33
+ for (const key in attributes) {
34
+ if (Object.prototype.hasOwnProperty.call(attributes, key)) {
35
+ this.element.setAttribute(key, attributes[key]);
36
+ }
37
+ }
38
+ return this;
39
+ }
40
+ onClick(handler) {
41
+ this.element.onclick = handler;
42
+ return this;
43
+ }
44
+ on(event, handler) {
45
+ this.element.addEventListener(event, handler);
46
+ return this;
47
+ }
48
+ render() {
49
+ return this.element;
50
+ }
51
+ }
52
+ // Factory functions for creating elements
53
+ const div = (...children) => new DomElement('div', ...children);
54
+ exports.div = div;
55
+ const h1 = (...children) => new DomElement('h1', ...children);
56
+ exports.h1 = h1;
57
+ const p = (...children) => new DomElement('p', ...children);
58
+ exports.p = p;
59
+ const a = (...children) => new DomElement('a', ...children);
60
+ exports.a = a;
61
+ const span = (...children) => new DomElement('span', ...children);
62
+ exports.span = span;
63
+ const img = () => new DomElement('img');
64
+ exports.img = img;
65
+ const input = () => new DomElement('input');
66
+ exports.input = input;
67
+ const button = (...children) => new DomElement('button', ...children);
68
+ exports.button = button;
69
+ // Base Component class for state management
70
+ class Component {
71
+ constructor(initialState = {}) {
72
+ this._mountedElement = null;
73
+ this._parentSelector = null;
74
+ this.state = initialState;
75
+ this.update = this.update.bind(this); // Bind update for callbacks
76
+ }
77
+ setState(newState) {
78
+ this.state = Object.assign(Object.assign({}, this.state), newState);
79
+ this.update(); // Trigger re-render
80
+ }
81
+ update() {
82
+ if (this._mountedElement && this._parentSelector) {
83
+ // Re-render the component and replace the old element
84
+ const newElement = this.render().render();
85
+ const parent = document.querySelector(this._parentSelector);
86
+ if (parent) {
87
+ parent.replaceChild(newElement, this._mountedElement);
88
+ this._mountedElement = newElement; // Update reference to the new element
89
+ }
90
+ }
91
+ }
92
+ // Method called by the top-level render function
93
+ _mount(selector) {
94
+ this._parentSelector = selector;
95
+ this._mountedElement = this.render().render();
96
+ return this._mountedElement;
97
+ }
98
+ }
99
+ exports.Component = Component;
100
+ // The top-level render function
101
+ function render(selector, component) {
102
+ const app = document.querySelector(selector);
103
+ if (app) {
104
+ app.innerHTML = '';
105
+ app.appendChild(component._mount(selector));
106
+ }
107
+ }
108
+ exports.render = render;
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "uigar",
3
+ "version": "0.3.0",
4
+ "description": "A simple and declarative JavaScript framework for building user interfaces.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "uigar": "./cli.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/vugarsafarzada/uigar.git"
18
+ },
19
+ "homepage": "https://github.com/vugarsafarzada/uigar#readme",
20
+ "keywords": [
21
+ "framework",
22
+ "ui",
23
+ "declarative",
24
+ "frontend"
25
+ ],
26
+ "author": "",
27
+ "license": "ISC",
28
+ "devDependencies": {
29
+ "typescript": "^4.5.0"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "cli.js",
34
+ "template"
35
+ ]
36
+ }
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>my-new-framework</title>
5
+ <link rel="stylesheet" href="./src/assets/styles/main.scss">
6
+ </head>
7
+ <body>
8
+ <div id="app"></div>
9
+ <script type="module" src="./src/index.ts"></script>
10
+ </body>
11
+ </html>
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "uigar-app",
3
+ "version": "0.2.0",
4
+ "description": "A new uigar application.",
5
+ "source": "index.html",
6
+ "scripts": {
7
+ "start": "parcel index.html",
8
+ "build": "parcel build index.html"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "ISC",
13
+ "type": "module",
14
+ "dependencies": {
15
+ "uigar": "^0.1.0"
16
+ },
17
+ "devDependencies": {
18
+ "@parcel/transformer-sass": "^2.16.3",
19
+ "parcel": "latest"
20
+ }
21
+ }
@@ -0,0 +1,22 @@
1
+ body {
2
+ background-color: #f0f0f0;
3
+ font-family: sans-serif;
4
+
5
+ h1 {
6
+ color: #333;
7
+ }
8
+ }
9
+
10
+ .btn {
11
+ padding: 10px 20px;
12
+ border: none;
13
+ border-radius: 5px;
14
+ background-color: #007bff;
15
+ color: white;
16
+ cursor: pointer;
17
+ font-size: 16px;
18
+
19
+ &:hover {
20
+ background-color: #0056b3;
21
+ }
22
+ }
@@ -0,0 +1,7 @@
1
+ import { button } from 'uigar';
2
+
3
+ export function Button(text: string, onClick: () => void) {
4
+ return button(text)
5
+ .onClick(onClick)
6
+ .settings({ class: 'btn' });
7
+ }
@@ -0,0 +1,12 @@
1
+ import { handleLocation, navigate } from './router';
2
+
3
+ window.addEventListener('popstate', handleLocation);
4
+ document.addEventListener('DOMContentLoaded', () => {
5
+ document.body.addEventListener('click', e => {
6
+ if (e.target && (e.target as HTMLElement).matches('[data-link]')) {
7
+ e.preventDefault();
8
+ navigate((e.target as HTMLAnchorElement).href);
9
+ }
10
+ });
11
+ handleLocation();
12
+ });
@@ -0,0 +1,11 @@
1
+ import { div, h1, p, a } from 'uigar';
2
+
3
+ export function AboutPage() {
4
+ return div(
5
+ h1('About Us'),
6
+ p('This is the about page. Add your content here.'),
7
+ a('Go to Home Page')
8
+ .settings({ href: '/', 'data-link': '' })
9
+ )
10
+ .settings({ class: 'about-page' });
11
+ }
@@ -0,0 +1,14 @@
1
+ import { div, h1, a } from 'uigar';
2
+ import { Button } from '../components/Button';
3
+
4
+ export function HomePage() {
5
+ return div(
6
+ h1('Welcome to the Home Page'),
7
+ Button('Click me!', () => {
8
+ alert('Button clicked!');
9
+ }),
10
+ a('Go to About Page')
11
+ .settings({ href: '/about', 'data-link': '' })
12
+ )
13
+ .settings({ class: 'home-page' });
14
+ }
@@ -0,0 +1,19 @@
1
+ import { HomePage } from './pages/HomePage';
2
+ import { AboutPage } from './pages/AboutPage';
3
+ import { render } from 'uigar';
4
+
5
+ const routes = {
6
+ '/': HomePage,
7
+ '/about': AboutPage,
8
+ };
9
+
10
+ export function handleLocation() {
11
+ const path = window.location.pathname;
12
+ const page = routes[path] || routes['/']; // Default to home page
13
+ render('#app', page());
14
+ }
15
+
16
+ export function navigate(path: string) {
17
+ window.history.pushState({}, '', path);
18
+ handleLocation();
19
+ }