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 +21 -0
- package/README.md +91 -0
- package/cli.js +43 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +108 -0
- package/package.json +36 -0
- package/template/index.html +11 -0
- package/template/package.json +21 -0
- package/template/src/assets/styles/main.scss +22 -0
- package/template/src/components/Button.ts +7 -0
- package/template/src/index.ts +12 -0
- package/template/src/pages/AboutPage.ts +11 -0
- package/template/src/pages/HomePage.ts +14 -0
- package/template/src/router.ts +19 -0
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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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,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,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
|
+
}
|