als-component
Advanced tools
Comparing version
class Component { | ||
constructor(componentName,fn,data) { | ||
this.start = Component.$genId() | ||
this.$id=0 | ||
this.componentName = Component.$firstUpper(componentName) | ||
this.componentName = componentName[0].toUpperCase() + componentName.substring(1) | ||
this.fn = fn | ||
this.data = data | ||
this.actions={} | ||
Component[this.componentName] = this | ||
if(window) { | ||
window['$C'+this.componentName] = this | ||
window['$c'+this.componentName] = this.actions | ||
} | ||
} | ||
@@ -14,13 +17,9 @@ | ||
update(data,id,updateElement=true) { | ||
if(id) { | ||
if(this[id] == undefined) this[id] = data | ||
else if(data == undefined) data = this[id] | ||
} else { | ||
if(this.data == undefined) this.data = data | ||
else if(data == undefined) data = this.data | ||
data = this.___getData(data,id) | ||
if(updateElement) { | ||
let element = this.element(id) | ||
if(element !== null) element.outerHTML = this.fn(data,id) | ||
return element | ||
} | ||
let element = this.element(id) | ||
if(element !== null && updateElement) { | ||
element.outerHTML = this.fn(data,id) | ||
} else return this.fn(data,id) | ||
return this.fn(data,id) | ||
} | ||
@@ -31,24 +30,27 @@ | ||
if(id) selector += `#${id}` | ||
try {return document.querySelector(selector) | ||
} catch {return null} | ||
let element = document.querySelector(selector) | ||
if(element == null && id) delete this[id] | ||
return element | ||
} | ||
genId(start=this.start) { | ||
let id = start+this.$id | ||
this.$id++ | ||
return id | ||
___getData(data,id) { | ||
let self = this | ||
if(id) { | ||
if(this[id] == undefined) this[id] = { | ||
id, | ||
data, | ||
get element() {return self.element(id)}, | ||
update:(data) => this.update(data,id,true), | ||
get:(data) => this.update(data,id,false), | ||
} | ||
else if(data !== undefined) this[id].data = data | ||
else if(data == undefined) data = this[id].data | ||
} else { | ||
if(this.data == undefined && data !== undefined) this.data = data | ||
if(data == undefined) data = this.data | ||
} | ||
return data | ||
} | ||
static $genId = (start='id') => `${start}${(Math.random() + 1).toString(36).substring(7)}` | ||
static $firstUpper = (string) => string[0].toUpperCase() + string.substring(1) | ||
static $ = (selector,source=document) => source.querySelector(selector) | ||
static $$ = (selector,source=document) => [...source.querySelectorAll(selector)] | ||
static $next = (element) => element.nextElementSibling | ||
static $prev = (element) => element.previousElementSibling | ||
static $parent = (element) => element.parentNode | ||
static $var(varName,varValue) { | ||
if(varValue == undefined) return getComputedStyle(document.documentElement).getPropertyValue('--'+varName) | ||
else document.documentElement.style.setProperty('--'+varName,varValue) | ||
return varValue | ||
} | ||
} | ||
try {window.$C = (componentName,fn,data) => new Component(componentName,fn,data)} catch {} | ||
try {module.exports = Component} catch{} |
function exportComponents(components = res.components,fns=res.fns) { | ||
let Component = require('./component') | ||
let result = '<script>' + Component.toString() | ||
fns.forEach(fn => { | ||
result += ` | ||
Component.${fn.name} = ${fn.toString()}` | ||
}); | ||
let fnsString = '' | ||
for(let componentName in components) { | ||
let {fn,data,update} = components[componentName] | ||
let varName = componentName.charAt(0).toUpperCase() + componentName.slice(1) | ||
result += ` | ||
let ${varName} = new Component('${varName}',${fn.toString()},${JSON.stringify(data)}) | ||
${update ? `${varName}.update()` : ''} | ||
new Component('${componentName}',${fn.toString()},${JSON.stringify(data)}) | ||
${update ? `.update()` : ''} | ||
` | ||
let curentFns = fns[componentName] | ||
curentFns.forEach(fn => { | ||
let Cn = componentName.charAt(0).toUpperCase() + componentName.slice(1); | ||
fnsString += `Component.${Cn}.actions.${fn.name} = ${fn.toString()} | ||
` | ||
}); | ||
} | ||
result += fnsString | ||
result += '</script>' | ||
@@ -22,5 +25,6 @@ return result | ||
res.components = {} | ||
res.fns = [] | ||
res.fns = {} | ||
res.component = function(componentName,fn,data,update=false) { | ||
res.components[componentName] = {fn,data,update} | ||
res.fns[componentName] = [] | ||
} | ||
@@ -27,0 +31,0 @@ res.exportComponents = function(components = res.components,fns=res.fns) { |
{ | ||
"name": "als-component", | ||
"version": "0.6.1", | ||
"version": "0.7.0", | ||
"description": "Managing components and states.", | ||
"main": "export.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"author": "Alex Sorkin", | ||
"license": "MIT" | ||
} |
327
readme.md
@@ -11,21 +11,14 @@ # Als-component | ||
*new in 0.5* | ||
* auto capitalize component name on constructor | ||
* ``component.genId(start)`` method - generating id and increasing it automaticly | ||
*new in 0.7* | ||
* all utilities removed (you can use als-dom instead) | ||
* window.$C function = new Component() | ||
* window.$C{ComponentName} | ||
* component.{$id} = {id,update,get,element} | ||
*new in 0.6* | ||
* component(object) methods: | ||
* get method | ||
* element method | ||
* Component (class) methods | ||
* $genId | ||
* $firstUpper | ||
* $ | ||
* $$ | ||
* $next | ||
* $prev | ||
* $parent | ||
* $var | ||
The version has tested - all should work. | ||
If not, please contact me - Alexsorkin1980@gmail.com | ||
**main** | ||
There are 3 files: | ||
@@ -36,8 +29,13 @@ 1. component.js - for frontend usage | ||
## Component.js | ||
Syntax: | ||
```javascript | ||
new Component(ComponentName,fn,data) | ||
.update(data,id,updateElement=true) | ||
.get(data,id) // => update(data,id,false) | ||
let c = new Component(ComponentName,fn,data) | ||
c.update(data,id,updateElement=true) // return created element | ||
c.get(data,id) // return string html | ||
c.element() // return component's element | ||
c[id] = {update,get,data,element,id} | ||
c.actions // empty object for action functions | ||
``` | ||
@@ -47,9 +45,10 @@ | ||
* ``new Component(ComponentName,fn,data)`` | ||
* **ComponentName** - Name of component, which will be available as Component.componentName | ||
* Has to start with capital (constructor will capitalize first letter anyway) | ||
* **ComponentName** - Name of component | ||
* Available as Component.ComponentName or as $CComponentName | ||
* **fn** - function(data,id) which has to return html code | ||
* html code has to be between ``<tag component="componentName" id="id"></tag>`` | ||
* html code has to be between ``<tag component="componentName" id="id"></tag>`` if you want to update the component (not to get it) | ||
* id parameter necessary only then you use more then one component | ||
* **data** - "the state" for component | ||
* available as ``Component.componentName.data`` or ``Component.componentName.id`` if there is id | ||
* available as ``Component.componentName.data`` or ``Component.componentName.id.data`` if there is id | ||
* ``update(data,id,updateElement=true)`` | ||
@@ -60,2 +59,4 @@ * **data** - updates the "state". | ||
* **id** - specify which data to use and which element to update | ||
* will create object componet.id= {id,data,element,update,get} | ||
* element is a getter | ||
* **updateElement** - if false, update will return fn result only (without updating html element) | ||
@@ -65,9 +66,17 @@ * true by default - updating data and html element | ||
### Name convention | ||
Component.Upper - Component | ||
Component.lower - fn | ||
Component.$fn - $utility | ||
* ``get(data,id)`` - same as update, but with ``updateElement=false`` | ||
### Example | ||
### window shortcuts | ||
Syntax | ||
```javascript | ||
let ComponentName = 'test' | ||
$C(ComponentName,fn,data) // equivalent to new Component(ComponentName,fn,data) | ||
$CTest // equivalent to Component.Test | ||
$cTest // equivalent to Component.Test.actions | ||
``` | ||
### Example simple counter | ||
```html | ||
@@ -78,56 +87,117 @@ <!-- Include component.js --> | ||
<!-- Here will appear App component--> | ||
<div component="app"></div> | ||
<div component="counter">0</div> | ||
<button onclick="$CCounter.update(1)">+</button> | ||
<button onclick="$CCounter.update(-1)">-</button> | ||
<script> | ||
// Creating component Test (must be before App) | ||
let Test = new Component('Test',function({testing,some},id) { | ||
return /*html*/`<div component="test"> | ||
test component | ||
<div>${testing}</div> | ||
<div>${some}</div> | ||
$C('counter',(add) =>{ | ||
let curent = parseInt($CCounter.element().innerHTML) | ||
return /*html*/`<div component="counter">${curent+add}</div>` | ||
}) | ||
</script> | ||
``` | ||
### Example todos | ||
```html | ||
<div component="todos"></div> | ||
<input type="text" onchange="$cTodos.addNew(this)"> | ||
<script> | ||
let todos = [ | ||
{title: "delectus aut autem",completed: true}, | ||
{title: "quis ut nam facilis et officia qui",completed: false}, | ||
{title: "fugiat veniam minus",completed: false} | ||
] | ||
$C('todo',function(todo,id){ | ||
let index = id.replace('todo','') | ||
return /*html*/`<div component="todo" id="${id}" > | ||
<span ${todo.completed ? 'style="text-decoration:line-through"' : ''} | ||
onclick="$cTodo.update('${id}')">${todo.title}</span> | ||
<button onclick="$cTodo.remove(${index})">×</button> | ||
</div>` | ||
}) | ||
// Creating component App with Test component inside | ||
new Component('App',function(data,id) { | ||
let testing = 'testing variable' | ||
return /*html*/`<div component="app"> | ||
${Test.get({testing,some:'some variable'},'test-id')} | ||
<input type="text" value="${testing}" | ||
oninput=" | ||
Test.data.testing = this.value | ||
Test.update() | ||
"> | ||
<div id="test">Hello from main component</div> | ||
$C('todos',function (data){ | ||
return /*html*/`<div component="todos"> | ||
${data.map((todo,index) => $CTodo.get( | ||
todo,'todo'+index | ||
)).join('')} | ||
</div>` | ||
}).update() // updating component App in html | ||
}).update(todos) | ||
// element method | ||
App.element() // return app html element | ||
Test.element('test-id') // return test html element | ||
$CTodos.actions.addNew = (element) => { | ||
$CTodos.data.unshift({title:element.value,completed:false}) | ||
$CTodos.update() | ||
element.value = '' | ||
} | ||
$CTodo.actions.update = (id) => { | ||
$CTodo[id].data.completed = !$CTodo[id].data.completed; | ||
$CTodo[id].update() | ||
} | ||
$CTodo.actions.remove = (index) => { | ||
$CTodos.data.splice(index,1) | ||
$CTodos.update() | ||
} | ||
</script> | ||
``` | ||
The example above, creates 2 components(App,Test) and inserting it to ``<div component="app"></div>``. | ||
### utility methods | ||
## router.js | ||
```js | ||
let {$genId,$firstUpper,$qs,$qsa,$next,$prev,$parent,$var} = Component | ||
$genId() // idsxiz5j | ||
$genId('test-') // test-sxiz5j | ||
$firstUpper('some'), // Some | ||
$('#test'), // $(selector,source=document) = document.querySelctor(selector) | ||
$$('.some'), // $$(selector,source=document) = document.querySelctorAll(selector) | ||
$next(dom), // = dom.nextElementSibling | ||
$prev(dom), // = dom.previousElementSibling | ||
$parent(dom), // = dom.parentNode | ||
$var('bg-color'), // getting value for css variable var(--bg-color) | ||
$var('bg-color','blue'), // set new value to var(--bg-color) | ||
router.js has function ``router(routes,defaultRoute)``. The function allows you to use html layout for js files and functions. | ||
**Parameters:** | ||
* routes - object where keys is a keys for ``?route=key`` and value is a src of js file or function. | ||
* defaultRoute - src of js file or function | ||
Here is example: | ||
```html | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<script src="/node_modules/als-component/component.js"></script> | ||
<script src="/node_modules/als-component/router.js"></script> | ||
<title>Some</title> | ||
</head> | ||
<body> | ||
<nav> | ||
<a href="?route=test">test</a> | ||
<a href="?route=test1">test1</a> | ||
</nav> | ||
<main component="main"></main> | ||
</body> | ||
<script> | ||
router({ | ||
test1:'/test1.js', | ||
test:() => new Componenet('test',/*html*/` | ||
<main component="main">Hello from test fn</main> | ||
`).update() | ||
},'/home.js') | ||
</script> | ||
</html> | ||
``` | ||
**test1.js** | ||
```javascript | ||
new Componenet('Test',/*html*/` | ||
<main component="main">Hello from test1.js</main> | ||
`).update() | ||
``` | ||
**home.js** | ||
```javascript | ||
Component.$(['component=main']).innerHTML = /*html*/` | ||
Home page - default page if route not found. | ||
` | ||
``` | ||
## export.js | ||
@@ -141,3 +211,3 @@ For using export with express you need to add it as middleware: | ||
res.component(componentName,fn,data,update=false) // creating components | ||
res.fns // array for functions which will be available on Component.fnName | ||
res.fns // object for actions | ||
return res.end(` | ||
@@ -150,2 +220,3 @@ <some html> | ||
Let's see how todo application will work with Component. The files we will need: | ||
@@ -155,16 +226,23 @@ 1. todo.js - component for single todo | ||
3. data.js - with todos array | ||
4. addnew.js - action for adding new todo | ||
4. actions.js - action for adding new todo | ||
5. server.js - server using express and running the app | ||
**data.js** | ||
```javascript | ||
module.exports = [ | ||
{title: "delectus aut autem",completed: true}, | ||
{title: "quis ut nam facilis et officia qui",completed: false}, | ||
{title: "fugiat veniam minus",completed: false} | ||
] | ||
``` | ||
**todo.js** | ||
```javascript | ||
module.exports = function(todo,id){ | ||
return /*html*/`<div component="todo" id="${id}" | ||
${todo.completed ? 'style="text-decoration:line-through"' : ''} | ||
onclick=" | ||
Todo['${id}'].completed = !Todo['${id}'].completed; | ||
Todo.update(undefined,'${id}') | ||
"> | ||
${todo.title} | ||
module.exports = function(todo,id) { | ||
let index = id.replace('todo','') | ||
return /*html*/`<div component="todo" id="${id}" > | ||
<span ${todo.completed ? 'style="text-decoration:line-through"' : ''} | ||
onclick="$cTodo.update('${id}')">${todo.title}</span> | ||
<button onclick="$cTodo.remove(${index})">×</button> | ||
</div>` | ||
@@ -176,7 +254,6 @@ } | ||
```javascript | ||
module.exports = function (data,id){ | ||
module.exports = function (data){ | ||
return /*html*/`<div component="todos"> | ||
${data.map(todo=> Component.Todo.get( | ||
todo, | ||
Component.Todo.genId('todo') | ||
${data.map((todo,index) => $CTodo.get( | ||
todo,'todo'+index | ||
)).join('')} | ||
@@ -187,17 +264,20 @@ </div>` | ||
**data.js** | ||
**actions.js** | ||
```javascript | ||
module.exports = [ | ||
{title: "delectus aut autem",completed: true}, | ||
{title: "quis ut nam facilis et officia qui",completed: false}, | ||
{title: "fugiat veniam minus",completed: false} | ||
] | ||
``` | ||
let addNew = (element) => { | ||
$CTodos.data.unshift({title:element.value,completed:false}) | ||
$CTodos.update() | ||
element.value = '' | ||
} | ||
**addnew.js** | ||
```javascript | ||
module.exports = function addNew(element) { | ||
Component.Todos.data.unshift({title:element.value,completed:false}) | ||
Component.Todos.update() | ||
let update = (id) => { | ||
$CTodo[id].data.completed = !$CTodo[id].data.completed; | ||
$CTodo[id].update() | ||
} | ||
let remove = (index) => { | ||
$CTodos.data.splice(index,1) | ||
$CTodos.update() | ||
} | ||
module.exports = {addNew,update,remove} | ||
``` | ||
@@ -211,7 +291,10 @@ | ||
app.get('/',(req,res) => { | ||
res.fns.push(require('./addnew')) // Adding function | ||
// Defining components | ||
res.component('Todo',require('./todo')) | ||
res.component('Todos',require('./todos'),require('./data'),true) | ||
// Defining components | ||
res.component('todo',require('./todo')) | ||
res.component('todos',require('./todos'),require('./data'),true) | ||
// Adding actions | ||
let {addNew,update,remove} = require('./actions') | ||
res.fns.todo.push(update) | ||
res.fns.todo.push(remove) | ||
res.fns.todos.push(addNew) | ||
return res.end(/*html*/`<!DOCTYPE html> | ||
@@ -224,3 +307,3 @@ <html lang="en"> | ||
<div component="todos"></div> | ||
<input type="text" onchange="Component.addNew(this)"> | ||
<input type="text" onchange="$cTodos.addNew(this)"> | ||
${res.exportComponents()} | ||
@@ -232,53 +315,1 @@ </body> | ||
``` | ||
## router.js | ||
router.js has function ``router(routes,defaultRoute)``. The function allows you to use html layout for js files and functions. | ||
**Parameters:** | ||
* routes - object where keys is a keys for ``?route=key`` and value is a src of js file or function. | ||
* defaultRoute - src of js file or function | ||
Here is example: | ||
```html | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<script src="/node_modules/als-component/component.js"></script> | ||
<script src="/node_modules/als-component/router.js"></script> | ||
<title>Some</title> | ||
</head> | ||
<body> | ||
<nav> | ||
<a href="?route=test">test</a> | ||
<a href="?route=test1">test1</a> | ||
</nav> | ||
<main component="main"></main> | ||
</body> | ||
<script> | ||
router({ | ||
test1:'/test1.js', | ||
test:() => new Componenet('test',/*html*/` | ||
<main component="main">Hello from test fn</main> | ||
`).update() | ||
},'/home.js') | ||
</script> | ||
</html> | ||
``` | ||
**test1.js** | ||
```javascript | ||
new Componenet('Test',/*html*/` | ||
<main component="main">Hello from test1.js</main> | ||
`).update() | ||
``` | ||
**home.js** | ||
```javascript | ||
Component.$(['component=main']).innerHTML = /*html*/` | ||
Home page - default page if route not found. | ||
` | ||
``` |
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
11913
4.91%95
6.74%2
-33.33%302
11.44%