├── LICENSE ├── README.md ├── package.json └── tiny.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Victor Ribeiro 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tiny.js 2 | 3 | **Tiny.js** is a minimalistic JavaScript framework designed for building reactive user interfaces with a functional programming approach. It provides the core functionality needed for dynamic DOM manipulation and reactive state management without the overhead of larger, more complex libraries. 4 | 5 | Tiny.js is ideal for small projects or when you want a lightweight solution to build interactive web apps with simple reactivity and clean code. 6 | 7 | ## Features 8 | 9 | ### 1. **Dynamic HTML Element Creation** 10 | 11 | Tiny.js makes it easy to create HTML elements with simple functions. Each HTML tag (e.g., `div`, `a`, `ul`, `li`, etc.) is mapped to a function that you can call to create an element of that type. You can pass an object of attributes and children, just like JSX in React, or simply append text nodes or other elements. 12 | 13 | ```javascript 14 | a({ href: 'https://example.com' }, 'Click me'); 15 | div({ class: 'container' }, 'Hello World'); 16 | ``` 17 | 18 | ### 2. **Reactive State Management** 19 | 20 | Tiny.js includes a simple, yet powerful, state management system that makes your application reactive. You can create state objects that automatically trigger updates when modified. This is done using JavaScript's `Proxy` to observe changes in the state and rerun any necessary updates. 21 | 22 | ```javascript 23 | const state = createState({ 24 | todos: [], 25 | }); 26 | 27 | state.addUpdate('todos', () => { 28 | console.log('Todos have changed:', state.todos); 29 | }); 30 | 31 | state.todos = ['Learn Tiny.js']; // Triggers the update 32 | ``` 33 | 34 | ### 3. **Event Handling** 35 | 36 | Attach event handlers to elements directly by passing them as attributes. For example, in the following code, clicking the button will update the state. 37 | 38 | ```javascript 39 | button({ onclick: () => alert('Button clicked!') }, 'Click me'); 40 | ``` 41 | 42 | ### 4. **Shorthand DOM Selection Helpers** 43 | 44 | Tiny.js provides two utility functions for selecting DOM elements: 45 | 46 | - `$` for selecting a single element by CSS selector (`document.querySelector`). 47 | - `$$` for selecting multiple elements by CSS selector (`document.querySelectorAll`). 48 | 49 | ```javascript 50 | const firstItem = $('li'); // Selects the first
  • element 51 | const allItems = $$('li'); // Selects all
  • elements 52 | ``` 53 | 54 | ### 5. **Minimalistic and Lightweight** 55 | 56 | Tiny.js is focused on simplicity and performance. It is just a few kilobytes in size, making it perfect for small projects or for when you need a minimal framework that doesn’t add unnecessary overhead. 57 | 58 | ## Example Usage 59 | 60 | Here’s an example of how you can build a simple Todo app with Tiny.js: 61 | 62 | ### `Todo.js` 63 | 64 | ```javascript 65 | export default function Todo(state, text, index) { 66 | return li(text, button({ onclick: function() { 67 | const todos = state.todos.slice(); 68 | todos.splice(index, 1); 69 | state.todos = todos; 70 | }}, 'x')); 71 | } 72 | ``` 73 | 74 | ### `TodoList.js` 75 | 76 | ```javascript 77 | import Todo from './Todo.js'; 78 | 79 | export default function TodoList(state) { 80 | const list = ul(); 81 | 82 | state.addUpdate('todos', function() { 83 | list.innerHTML = ''; 84 | state.todos.forEach((t, i) => list.append(Todo(state, t, i))); 85 | }); 86 | 87 | return list; 88 | } 89 | ``` 90 | 91 | ### `CreateTodo.js` 92 | 93 | ```javascript 94 | export default function CreateTodo(state) { 95 | const inp = input(); 96 | return form( 97 | { onsubmit: function(e) { 98 | e.preventDefault(); 99 | state.todos = [...state.todos, inp.value]; 100 | inp.value = ''; 101 | }}, 102 | inp, 103 | input({ type: 'submit', value: 'Add Todo' }) 104 | ); 105 | } 106 | ``` 107 | 108 | ### `TodoApp.js` 109 | 110 | ```javascript 111 | import './tiny.js'; 112 | import CreateTodo from './CreateTodo.js'; 113 | import TodoList from './TodoList.js'; 114 | 115 | export default function TodoApp() { 116 | const state = createState({ todos: [] }); 117 | 118 | const style = { 119 | display: 'flex', 120 | alignItems: 'center', 121 | flexDirection: 'column' 122 | }; 123 | 124 | return div( 125 | { style }, 126 | CreateTodo(state), 127 | TodoList(state) 128 | ); 129 | } 130 | ``` 131 | 132 | ## Live Examples 133 | - [TinyApp](https://github.com/victorqribeiro/TinyApp) 134 | - [TodoApp](https://github.com/victorqribeiro/TodoApp) 135 | 136 | ## How It Works 137 | 138 | - **Tiny.js provides a minimal set of functions** to create HTML elements (`a()`, `div()`, `ul()`, etc.) and manipulate the DOM with minimal syntax. 139 | - **State management** is handled through a `createState` function, which wraps state objects with a Proxy. This allows for automatic reactivity: when a property is modified, all listeners are notified and the UI is updated accordingly. 140 | - **Event handling** is done by passing event listeners as object properties when creating DOM elements. 141 | - **Minimal dependencies** and a **small footprint** ensure that Tiny.js is lightweight and quick to integrate into any project. 142 | 143 | 144 | TinyJS attaches functions for each HTML tag (like `div`, `span`, `a`, etc.) to the global window object. You can create elements by simply calling the tag name as a function, passing in optional properties and child elements. 145 | 146 | Additionally, TinyJS introduces two new helper functions: 147 | - **`$`**: A wrapper around `document.querySelector`, simplifying DOM element selection. 148 | Example: 149 | ```javascript 150 | const post = $('#post_1'); // Selects the element with id 'post_1' 151 | ``` 152 | 153 | - **`$$()`**: A wrapper around `document.querySelectorAll` that returns an array of DOM elements, allowing for easy iteration. 154 | Example: 155 | ```javascript 156 | const items = $$('.item'); // Selects all elements with class 'item' 157 | items.forEach(item => { 158 | console.log(item); 159 | }); 160 | ``` 161 | 162 | ### Example 163 | 164 | ```javascript 165 | const myDiv = div( 166 | {id: 'container', className: 'my-class'}, 167 | h1('Hello World'), 168 | p('This is a dynamically generated paragraph.') 169 | ); 170 | 171 | document.body.appendChild(myDiv); 172 | ``` 173 | 174 | In this example, the `div`, `h1`, and `p` elements are created dynamically with their attributes and content specified as arguments. 175 | 176 | ### Advanced Example 177 | 178 | You can deeply assign properties to elements: 179 | 180 | ```javascript 181 | const styledButton = button( 182 | {style: {backgroundColor: 'blue', color: 'white'}, onclick: () => alert('you clicked me')}, 183 | 'Click Me!' 184 | ); 185 | 186 | document.body.appendChild(styledButton); 187 | ``` 188 | 189 | In this example, the `button` element is styled directly using the `style` property. 190 | 191 | ### Installation 192 | Simply include the **tiny.js** file in your project: 193 | 194 | ```html 195 | 196 | ``` 197 | 198 | [npm package](https://www.npmjs.com/package/tinyjs-framework) 199 | 200 | ```bash 201 | npm install tinyjs-framework 202 | ``` 203 | 204 | 205 | ## Supported Tags 206 | 207 | TinyJS supports a wide range of HTML tags, including: 208 | - Basic text elements: `p`, `span`, `strong`, `em`, etc. 209 | - Interactive elements: `button`, `input`, `select`, etc. 210 | - Media elements: `img`, `audio`, `video`, etc. 211 | - Container elements: `div`, `section`, `article`, `footer`, etc. 212 | 213 | ## Contribution 214 | 215 | Make sure to open an issue before submitting a PR. 216 | 217 | ## TypeScript 218 | 219 | If you want a typed version of this library, use this [fork](https://github.com/guilhermebolfe11/TinyTS) 220 | 221 | 222 | ## License 223 | 224 | Tiny.js is released under the MIT license. 225 | 226 | --- 227 | 228 | Tiny.js is designed for developers who want a small, functional, and easy-to-use framework for building reactive UIs without the complexity of larger frameworks. Whether you're building a small app or need something lightweight to get the job done, Tiny.js is the tool for you. 229 | 230 | _This README was generated by ChatGPT_ 231 | 232 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tinyjs-framework", 3 | "version": "1.1.3", 4 | "description": "Tiny JavaScript framework", 5 | "main": "tiny.js", 6 | "type": "module", 7 | "exports": { 8 | "import": "./tiny.js", 9 | "require": "./tiny.js" 10 | }, 11 | "files": ["tiny.js"], 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/victorqribeiro/TinyJS.git" 15 | }, 16 | "keywords": [ 17 | "javascript", 18 | "framework" 19 | ], 20 | "author": "Victor Ribeiro", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/victorqribeiro/TinyJS/issues" 24 | }, 25 | "homepage": "https://github.com/victorqribeiro/TinyJS#readme" 26 | } 27 | -------------------------------------------------------------------------------- /tiny.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | const assignDeep = (elm, props) => Object.entries(props).forEach(([key, value]) => { 3 | if (typeof value === 'object') return assignDeep(elm[key], value) 4 | try { Object.assign(elm, {[key]: value}) } catch { elm.setAttribute(key, value) } 5 | }) 6 | Array.from([ 7 | 'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 8 | 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 9 | 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 10 | 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 11 | 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 12 | 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 13 | 'kbd', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'meta', 14 | 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 15 | 'p', 'param', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 16 | 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 17 | 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 18 | 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 19 | 'ul', 'var', 'video', 'wbr' 20 | ]).forEach(tag => window[tag] = function(...args) { 21 | const props = typeof args[0] == 'object' && !(args[0] instanceof HTMLElement) ? args.shift() : null 22 | const elm = document.createElement(tag) 23 | props && assignDeep(elm, props) 24 | elm.append(...args.map(a => typeof a == 'string' ? document.createTextNode(a) : a)) 25 | return elm 26 | }) 27 | window['$'] = selector => document.querySelector(selector) 28 | window['$$'] = selector => Array.from(document.querySelectorAll(selector)) 29 | window['createState'] = state => { 30 | state._updates = Object.fromEntries(Object.keys(state).map(s => [s, []])) 31 | state._update = s => state._updates[s].forEach(u => u()) 32 | state.addUpdate = (s, u) => state._updates[s].push(u) 33 | return new Proxy(state, {set(o, p, v) {o[p] = v; o._update(p); return true}}) 34 | } 35 | })() 36 | --------------------------------------------------------------------------------