Skip to content

Commit

Permalink
Merge pull request #21 from ProjectPenrose/develop
Browse files Browse the repository at this point in the history
v0.4.0
  • Loading branch information
alexsc6955 authored Jan 17, 2024
2 parents 2d10e4b + e0a3dd5 commit 799c0a2
Show file tree
Hide file tree
Showing 54 changed files with 776 additions and 532 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.4.0] - 2024-01-17
### Added
- `Paradox.buildApp` function to create reactive components

### Changed
- Typescript types

## [0.3.5] - 2024-01-13
### Added
- Docs for the paradox-app example
Expand Down
48 changes: 15 additions & 33 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
·
<a href="https://github.com/ProjectPenrose/paradox/labels/enhancement">Request Features</a>
</p>
<p align="center">
<img alt="GitHub contributors" src="https://img.shields.io/github/contributors/ProjectPenrose/paradox">
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/w/ProjectPenrose/paradox">
<img alt="GitHub issues" src="https://img.shields.io/github/issues-raw/ProjectPenrose/paradox">
<img alt="GitHub pull requests" src="https://img.shields.io/github/issues-pr/ProjectPenrose/paradox">
</p>
<p align="center">
<b>IMPORTANT: Things are changing a lot right now, so please be patient, this is a work in progress</b>
</p>
Expand All @@ -32,7 +38,6 @@
- [Project structure example](#project-structure-example)
- [Webpack config](#webpack-config)
- [Paradox as a module in a simple html project](#paradox-as-a-module-in-a-simple-html-project)
- [Paradox on development mode](#paradox-on-development-mode)
- [Documentation](#documentation)
- [Build an element with `Paradox.buildElement`](#build-an-element-with-paradoxbuildelement)
- [Routes with `Paradox.Router`](#routes-with-paradoxrouter)
Expand All @@ -54,11 +59,11 @@ Contributions are welcome thogh xd, please [start a discussion](https://github.c

## Getting Started

Paradox is a simple vanilla javascript library for DOM manipulation that also provides a simple router and pubsub implementation.
Paradox is a simple vanilla javascript library for DOM manipulation that also provides a simple router and PubSub implementation.

### Requirements

- [Node.js](https://nodejs.org/en/) >= v18.17.1 (For development mode)
- [Node.js](https://nodejs.org/en/) >= v16.16.0 (For development mode)

### Installation

Expand All @@ -68,11 +73,10 @@ Paradox is a simple vanilla javascript library for DOM manipulation that also pr
```
2. Install NPM packages
```sh
cd penrose-paradox
npm install
```



## Usage

Now things can change depending on what you want to do, so here are some options:
Expand Down Expand Up @@ -156,38 +160,16 @@ module.exports = {
}
```
### Paradox as a module in a simple html project
### Paradox as a module in a simple HTML project
If you want to use it as a module in a simple html project, you should import it in your script like this:
If you want to use it as a module in a simple HTML project, you should import it in your script like this:
```html
<script type="module">
import Paradox from "path/to/paradox/build/index.js";
import Paradox from "./path/to/penrose-paradox/build/index.js";
</script>
```
### Paradox on development mode
If you want to build a project that uses paradox, you can run the build script:
```sh
npm run paradox-app
```
**This will generate the following:**
- A `server` folder with a simple express server that serves the `dist` folder and redirects all the requests to the `index.html` file.
- A `scss` folder with a simple `main.scss` file that imports bootstrap.
- An `app` folder with a simple html project that uses paradox and a `main.js` file that imports paradox and shows a simple example of how to use it.
- A `webpack.config.js` file that you can use to build your project.
Then, the script will run the dev script, so you can start developing your project by changing the `app` folder and running the dev script again.
**This will generate a `dist` folder with the built project.**
**Notes:**
- The `server` folder is just a simple example, you can delete it and create your own server.
- The `scss` folder is just a simple example, you can delete it and create your own scss files.
- The `app` folder is just a simple example, you can delete it and create your own project.
- The `webpack.config.js` file is just a simple example, you can delete it and create your own webpack config file.
- The `dist` folder will contain the javascript bundle and a css file with the styles. (The css file is generated from the `scss/main.scss` file)
## Documentation
Paradox includes the following features:
Expand All @@ -209,7 +191,7 @@ Paradox provides a simple way to build an element with the `buildElement` functi
| children | array | The element children |
```javascript
import Paradox from "paradox";
import Paradox from "penrose-paradox";
function handleButtonClick() {
alert("Hello World!");
Expand Down Expand Up @@ -252,7 +234,7 @@ document.body.appendChild(Paradox.buildElement("button", myButton));
Paradox provides a simple router to handle the navigation between pages.
```javascript
import Paradox from "paradox";
import Paradox from "penrose-paradox";
function Home(props) {
const { root } = props;
Expand Down Expand Up @@ -319,7 +301,7 @@ In the PubSub pattern, publishers send messages without knowing who the subscrib
This pattern is widely used in event-driven programming and can help to decouple the components of an application, leading to code that is easier to maintain and extend. In the context of Paradox, it allows components to communicate with each other in a decoupled manner.

```javascript
import Paradox from "paradox";
import Paradox from "penrose-paradox";
Paradox.pubsub.subscribe("myEvent", (data) => {
console.log(data);
Expand Down
13 changes: 11 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The following features are not directly related to the library itself, but are s
- [ ] Finish community guidelines
- [ ] Automate release process
- [ ] Automate changelog generation
- [ ] Add badges to README
- [x] Add badges to README

### Features

Expand All @@ -26,7 +26,8 @@ The following features are planned to be implemented before the first stable rel

- [x] Add typescript watch mode to `npm run dev`
- [ ] Add state management support
- [ ] Add `Paradox.app`. This will allow users to create reactive components.
- [X] Add `Paradox.buildApp`. This will allow users to create reactive components.
- [ ] Add `Paradox.buildApp` tests

#### buildElement

Expand All @@ -41,3 +42,11 @@ The following features are planned to be implemented before the first stable rel
- [ ] Add `once` method so that a subscriber can be removed after it has been called once
- [ ] Add `clear` method to remove all subscribers

#### utils

- [ ] Add `debounce` method
- [ ] Add `throttle` method

#### Docs

- [ ] Turn examples into docs
2 changes: 2 additions & 0 deletions build/core/buildApp/helpers/buildVirtualDOM.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { ParadoxElement, ParadoxVirtualElement } from "../types";
export default function buildVirtualDOM(vTree: ParadoxElement | ParadoxElement[]): ParadoxVirtualElement[];
29 changes: 29 additions & 0 deletions build/core/buildApp/helpers/buildVirtualDOM.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const createElement_1 = __importDefault(require("./createElement"));
function buildVirtualDOM(vTree) {
let vDOM = [];
if (!Array.isArray(vTree))
vTree = [vTree];
for (const elementObj of vTree) {
let elementObject = elementObj;
if (typeof elementObj === "function")
elementObject = elementObj();
if (typeof elementObject === "string") {
vDOM.push(elementObject);
continue;
}
for (const [key, value] of Object.entries(elementObject)) {
let { attrs = {}, events = {}, children = [] } = value;
if (children.length) {
children = buildVirtualDOM(children);
}
vDOM.push((0, createElement_1.default)(key, { attrs, events, children }));
}
}
return vDOM;
}
exports.default = buildVirtualDOM;
2 changes: 2 additions & 0 deletions build/core/buildApp/helpers/createElement.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { ParadoxElement, ParadoxVirtualElement } from '../types';
export default function createElement(tagName: string, options?: ParadoxElement): ParadoxVirtualElement;
22 changes: 22 additions & 0 deletions build/core/buildApp/helpers/createElement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function createElement(tagName, options = { children: [], events: {}, attrs: {} }) {
if (!tagName)
throw new Error("tagName is required");
let children = [];
let events = {};
let attrs = {};
if (typeof options === 'object' && !Array.isArray(options) && 'children' in options) {
children = options.children || [];
events = options.events || {};
attrs = options.attrs || {};
}
return {
tagName,
attrs,
children,
events,
};
}
exports.default = createElement;
;
2 changes: 2 additions & 0 deletions build/core/buildApp/helpers/createVirtualDOM.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { ParadoxElement } from "../types";
export default function createVirtualDOM(treeFunc: Function): ParadoxElement | ParadoxElement[];
6 changes: 6 additions & 0 deletions build/core/buildApp/helpers/createVirtualDOM.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function createVirtualDOM(treeFunc) {
return treeFunc();
}
exports.default = createVirtualDOM;
2 changes: 2 additions & 0 deletions build/core/buildApp/helpers/diff.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { ParadoxVirtualElement, Patch } from "../types";
export default function diff(originalOldTree: ParadoxVirtualElement[], originalNewTree: ParadoxVirtualElement[]): Patch;
95 changes: 95 additions & 0 deletions build/core/buildApp/helpers/diff.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const render_1 = __importDefault(require("./render"));
function zip(xs, ys) {
const zipped = [];
for (let i = 0; i < Math.min(xs.length, ys.length); i++) {
zipped.push([xs[i], ys[i]]);
}
return zipped;
}
function diffAttrs(oldAttrs, newAttrs) {
const patches = [];
for (const [key, value] of Object.entries(newAttrs)) {
patches.push(node => {
node.setAttribute(key, value);
return node;
});
}
for (const key of Object.keys(oldAttrs)) {
if (!(key in newAttrs)) {
patches.push(node => {
node.removeAttribute(key);
return node;
});
}
}
return (node) => {
for (const patch of patches) {
patch(node);
}
};
}
function diffChildren(oldChildren, newChildren) {
const patches = [];
for (const [oldChild, newChild] of zip(oldChildren, newChildren)) {
patches.push(diff(oldChild, newChild));
}
const additionalPatches = [];
for (const additionalChild of newChildren.slice(oldChildren.length)) {
additionalPatches.push(node => {
node.appendChild((0, render_1.default)(additionalChild));
return node;
});
}
return (parent) => {
for (const [patch, child] of zip(patches, parent.childNodes)) {
patch(child);
}
for (const patch of additionalPatches) {
patch(parent);
}
return parent;
};
}
function diff(originalOldTree, originalNewTree) {
const oldTree = originalOldTree[0];
const newTree = originalNewTree[0];
if (!newTree) {
return (node) => {
node.remove();
return undefined;
};
}
if (typeof oldTree === "string" || typeof newTree === "string") {
if (oldTree !== newTree) {
return (node) => {
const newNode = (0, render_1.default)(newTree);
node.replaceWith(newNode);
return newNode;
};
}
else {
return (node) => undefined;
}
}
if (oldTree.tagName !== newTree.tagName) {
return (node) => {
const newNode = (0, render_1.default)(newTree);
node.replaceWith(newNode);
return newTree;
};
}
const patchAttr = diffAttrs(oldTree.attrs, newTree.attrs);
const patchChildren = diffChildren(oldTree.children, newTree.children);
return (node) => {
patchAttr(node);
patchChildren(node);
return node;
};
}
exports.default = diff;
;
1 change: 1 addition & 0 deletions build/core/buildApp/helpers/mount.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function mount(vnode: HTMLElement, target: HTMLElement): HTMLElement;
7 changes: 7 additions & 0 deletions build/core/buildApp/helpers/mount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function mount(vnode, target) {
target.replaceWith(vnode);
return vnode;
}
exports.default = mount;
2 changes: 2 additions & 0 deletions build/core/buildApp/helpers/render.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { ParadoxVirtualElement } from "../types";
export default function render(vnode: ParadoxVirtualElement): HTMLElement | Text;
43 changes: 43 additions & 0 deletions build/core/buildApp/helpers/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function renderEle(vnode) {
let tagName = "";
let attrs = {};
let children = [];
let events = {};
if (typeof vnode === "object") {
tagName = vnode.tagName;
attrs = vnode.attrs;
children = vnode.children;
events = vnode.events || {};
}
const element = document.createElement(tagName);
for (const [key, value] of Object.entries(attrs)) {
element.setAttribute(key, value.toString());
}
for (const child of children) {
const $child = render(child);
element.appendChild($child);
}
for (const [key, value] of Object.entries(events)) {
if (Array.isArray(value)) {
for (const event of value) {
element.addEventListener(key, event);
}
continue;
}
else {
element.addEventListener(key, value);
}
}
return element;
}
;
function render(vnode) {
if (typeof vnode === "string") {
return document.createTextNode(vnode);
}
return renderEle(vnode);
}
exports.default = render;
;
4 changes: 4 additions & 0 deletions build/core/buildApp/helpers/renderVirtualDOM.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { ParadoxVirtualElement } from '../types';
export declare let targetNodeCache: HTMLElement;
export declare function setTargetNodeCache(targetNode: HTMLElement): void;
export default function renderVirtualDOM(vDOM: ParadoxVirtualElement[], targetNode: HTMLElement): void;
Loading

0 comments on commit 799c0a2

Please sign in to comment.