@dogiloki/artha-js 1.0.0 → 1.2.1

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/dist/artha.min.js CHANGED
@@ -1,32 +1,40 @@
1
- import Config from '../src/core/Config.js';
2
1
  import Util from '../src/core/Util.js';
3
2
  import EventBus from '../src/core/EventBus.js';
4
3
  import TaskQueue from '../src/core/TaskQueue.js';
5
4
  import XHR from '../src/core/XHR.js';
6
5
  import ArthaMessage from '../src/components/artha-message.js';
6
+ import ArthaLoader from '../src/components/artha-loader.js';
7
7
  import ArthaContainer from '../src/components/artha-container.js';
8
8
  import ArthaForm from '../src/components/artha-form.js';
9
9
 
10
- if(!customElements.get('artha-container')){
11
- customElements.define('artha-container',ArthaContainer);
12
- }
13
- if(!customElements.get('artha-form')){
14
- customElements.define('artha-form',ArthaForm);
15
- }
16
- if(!customElements.get('artha-message')){
17
- customElements.define('artha-message',ArthaMessage);
18
- }
19
-
20
- const Artha={
21
- version:"1.0.0",
22
- config(options){
23
- Config.set(options);
24
- },
25
- get(path,def=null){
26
- return Config.get(path,def);
27
- }
10
+ export {
11
+ Util,
12
+ EventBus,
13
+ TaskQueue,
14
+ XHR,
15
+ ArthaMessage,
16
+ ArthaLoader,
17
+ ArthaContainer,
18
+ ArthaForm
28
19
  };
29
20
 
30
- Window.Artha=Artha;
21
+ Promise.resolve().then(()=>{
22
+ EventBus.emit('artha:before-register',{});
23
+ registerComponents();
24
+ EventBus.emit('artha:after-register',{});
25
+ });
31
26
 
32
- export default Artha;
27
+ function registerComponents(){
28
+ if(!customElements.get('artha-container')){
29
+ customElements.define('artha-container',ArthaContainer);
30
+ }
31
+ if(!customElements.get('artha-form')){
32
+ customElements.define('artha-form',ArthaForm);
33
+ }
34
+ if(!customElements.get('artha-message')){
35
+ customElements.define('artha-message',ArthaMessage);
36
+ }
37
+ if(!customElements.get('artha-loader')){
38
+ customElements.define('artha-loader',ArthaLoader);
39
+ }
40
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dogiloki/artha-js",
3
- "version": "1.0.0",
3
+ "version": "1.2.1",
4
4
  "description": "",
5
5
  "main": "dist/artha.min.js",
6
6
  "type": "module",
@@ -9,7 +9,9 @@
9
9
  "src"
10
10
  ],
11
11
  "scripts": {
12
- "dev": "sass --watch src/scss/main.scss:dist/artha.css",
12
+ "dev:css": "sass --watch src/scss/main.scss:dist/artha.min.css --style=compressed",
13
+ "dev:server": "live-server --watch=. --port=8080",
14
+ "dev": "concurrently \"npm run dev:css\" \"npm run dev:server\"",
13
15
  "build": "sass src/scss/main.scss dist/artha.min.css --style=compressed"
14
16
  },
15
17
  "keywords": [
@@ -27,5 +29,10 @@
27
29
  "bugs": {
28
30
  "url": "https://github.com/dogiloki/artha-js/issues"
29
31
  },
30
- "homepage": "https://github.com/dogiloki/artha-js#readme"
31
- }
32
+ "homepage": "https://github.com/dogiloki/artha-js#readme",
33
+ "devDependencies": {
34
+ "concurrently": "^9.2.1",
35
+ "live-server": "^1.2.2",
36
+ "sass": "^1.98.0"
37
+ }
38
+ }
@@ -0,0 +1,201 @@
1
+ export default class BaseComponent extends HTMLElement{
2
+
3
+ static counter=0;
4
+
5
+ // Método estático para definir atributos observados
6
+ static get observedAttributes(){
7
+ return [];
8
+ }
9
+
10
+ constructor(props=null,options={}){
11
+ super();
12
+ BaseComponent.counter+=1;
13
+ // Cache de elementos DOM referenciados por ID
14
+ this._elements={};
15
+ // Flag para evitar ciclos de actualización
16
+ this._updating=false;
17
+ // Mapeo de atributos
18
+ this._props=[];
19
+ // Mapeo para indicar comportamiento especiales en el mapeo de atributos
20
+ this._special_props={
21
+ booleans:[], // Propiedades booleanas
22
+ element_refs:[], // Propiedades de referencian elementos por ID
23
+ defaults:{}, // Propiedades con valor por defecto
24
+ resolvers:{} // Propiedades con un callback
25
+ };
26
+ this.configureProperties(props,options);
27
+ }
28
+
29
+ connectedCallback(){
30
+ this.onConnected();
31
+ this.dispatchEvent(new CustomEvent('component-ready',{
32
+ detail:this,
33
+ bubbles:true
34
+ }));
35
+ }
36
+ onConnected(){}
37
+
38
+ disconnectedCallback(){
39
+ this.onDisconnected();
40
+ this.clearElementCache();
41
+ }
42
+ onDisconnected(){}
43
+
44
+ attributeChangedCallback(name,old_value,new_value){
45
+ if(old_value===new_value) return;
46
+ if(this._props.includes(name)){
47
+ this._triggerUpdate(name,this._getPropertyValue(name));
48
+ }
49
+ this.onAttributeChanged(name,old_value,new_value);
50
+ }
51
+ onAttributeChanged(name,old_value,new_value){}
52
+
53
+ configureProperties(props,options={}){
54
+ if(props==null) return;
55
+ this._props=props;
56
+ this._special_props={
57
+ booleans:options.booleans||[],
58
+ element_refs:options.element_refs||[],
59
+ defaults:options.defaults||{},
60
+ resolvers:options.resolvers||{}
61
+ };
62
+ this._setupProperties();
63
+ }
64
+
65
+ _setupProperties(){
66
+ this._props.forEach((prop)=>{
67
+ Object.defineProperty(this,prop,{
68
+ get:()=>this._getPropertyValue(prop),
69
+ set:(value)=>this._setPropertyValue(prop,value),
70
+ enumerable:true,
71
+ configurable:true
72
+ });
73
+ });
74
+ // Iniciar con valores de atributos existentes
75
+ this._initializeProperties();
76
+ }
77
+
78
+ _getPropertyValue(prop){
79
+ // Propiedade que referencia un elemento por ID
80
+ if(this._special_props.element_refs.includes(prop)){
81
+ const element_id=this.getAttribute(prop);
82
+ if(!element_id) return null;
83
+ // Cache para evitar múltiples búsquedas en el DOM
84
+ if(!this._elements[prop] || this._elements[prop].id!==element_id){
85
+ this._elements[prop]=document.getElementById(element_id);
86
+ }
87
+ return this._elements[prop];
88
+ }
89
+ // Propiedades con callback
90
+ if(this._special_props.resolvers[prop]){
91
+ const raw_value=this.getAttribute(prop);
92
+ return this._special_props.resolvers[prop].get(raw_value,this);
93
+ }
94
+ // Propiedad booleana
95
+ if(this._special_props.booleans.includes(prop)){
96
+ return this.hasAttribute(prop) && this.getAttribute(prop)!=='false';
97
+ }
98
+ // Propiedad con valor por defecto
99
+ const value=this.getAttribute(prop);
100
+ if(value===null && this._special_props.defaults[prop]!==undefined){
101
+ return this._special_props.defaults[prop];
102
+ }
103
+ return value;
104
+ }
105
+
106
+ _setPropertyValue(prop,value){
107
+ if(this._updating) return;
108
+ const current_value=this.getAttribute(prop);
109
+ let new_value=value;
110
+ // Convertir a string para atributos
111
+ if(value===null || value===undefined){
112
+ new_value=null;
113
+ }else if (typeof value==='boolean'){
114
+ new_value=value?'':null;
115
+ }else if(typeof value==='object'){
116
+ // Para objetos como referencia a elementos, no actualizar el atributo
117
+ this._elements[prop]=value;
118
+ this._triggerUpdate(prop,value);
119
+ return;
120
+ }else if(this._special_props.resolvers[prop]){
121
+ new_value=this._special_props.resolvers[prop].set(value,this);
122
+ this._triggerUpdate(prop,value);
123
+ }else{
124
+ new_value=String(value);
125
+ }
126
+ // Solo actualizar si cambió
127
+ if(current_value!==new_value){
128
+ if(new_value===null){
129
+ this.removeAttribute(prop);
130
+ }else{
131
+ this.setAttribute(prop,new_value);
132
+ }
133
+ this._triggerUpdate(prop,value);
134
+ }
135
+ }
136
+
137
+ _initializeProperties(){
138
+ this._props.forEach((prop)=>{
139
+ const attrib_value=this.getAttribute(prop);
140
+ if(attrib_value!==null){
141
+ this[prop]=this._getPropertyValue(prop);
142
+ }else if(this._special_props.defaults[prop]!==undefined){
143
+ this[prop]=this._special_props.defaults[prop];
144
+ }
145
+ })
146
+ }
147
+
148
+ _triggerUpdate(prop,value){
149
+ if(this._updating) return;
150
+ this._updating=true;
151
+ // Evento específico para cambio de la propiedad
152
+ this.dispatchEvent(new CustomEvent(`${prop}-changed`,{
153
+ detail:{
154
+ property:prop,
155
+ value,
156
+ component:this
157
+ },
158
+ bubbles:true
159
+ }));
160
+ // Evento genérico para cualquier cambio
161
+ this.dispatchEvent(new CustomEvent('property-changed',{
162
+ detail:{
163
+ property:prop,
164
+ value,
165
+ component:this
166
+ },
167
+ bubbles:true
168
+ }));
169
+ // Actualización (puede ser sobrescrito)
170
+ this.onPropertyChanged(prop,value);
171
+ this._updating=false;
172
+ }
173
+
174
+ onPropertyChanged(prop,value){
175
+
176
+ }
177
+
178
+ setProperties(props){
179
+ this._updating=true;
180
+ Object.entries(props).forEach(([key,value])=>{
181
+ if(this._props.includes(key)){
182
+ this[key]=value;
183
+ }
184
+ });
185
+ this._updating=false;
186
+ this._triggerUpdate('batch',props);
187
+ }
188
+
189
+ getProperties(){
190
+ const props={};
191
+ this._props.forEach((prop)=>{
192
+ props[prop]=this[prop];
193
+ });
194
+ return props;
195
+ }
196
+
197
+ clearElementCache(){
198
+ this._elements={};
199
+ }
200
+
201
+ }
@@ -1,13 +1,25 @@
1
+ import BaseComponent from '../abstract/BaseComponent.js';
1
2
  import Util from '../core/Util.js';
2
3
  import EventBus from '../core/EventBus.js';
3
4
  import XHR from '../core/XHR.js';
4
5
  import TaskQueue from '../core/TaskQueue.js';
5
6
  import ArthaMessage from './artha-message.js';
6
7
 
7
- export default class ArthaContainer extends HTMLElement{
8
+ export default class ArthaContainer extends BaseComponent{
8
9
 
9
10
  constructor(){
10
- super();
11
+ super(
12
+ ["template","action","action_router","method",
13
+ "pagination","message","searcher","selectable","multiple"],
14
+ {
15
+ booleans:['searcher','selectable','multiple'],
16
+ element_refs:['template','mesage'],
17
+ defaults:{
18
+ method:'GET',
19
+ pagination:10
20
+ }
21
+ }
22
+ );
11
23
 
12
24
  this.onRenderItem=(element,data)=>{};
13
25
  this.onRenderItemFill=(element,data,fill_element,fill_data)=>{};
@@ -17,37 +29,8 @@ export default class ArthaContainer extends HTMLElement{
17
29
  this.items={};
18
30
  this.selection_store=new SelectionStore();
19
31
  this.response_type='json';
20
- this._elements={};
21
- this._props=[
22
- "template","action","action_router","method",
23
- "pagination","message","searcher","selectable","multiple"];
24
- this._props.forEach((prop)=>{
25
- Object.defineProperty(this,prop,{
26
- get:()=>{
27
- switch(prop){
28
- case 'template':
29
- if(!this._elements[prop] || this._elements[prop].id!==this.getAttribute(prop)){
30
- this._elements[prop]=document.getElementById(this.getAttribute(prop));
31
- }
32
- return this._elements[prop];
33
- case 'method': return this.getAttribute(prop)??'GET';
34
- default: return this.getAttribute(prop);
35
- }
36
- },
37
- set:(value)=>{
38
- if(this.getAttribute(prop)!==value){
39
- this.setAttribute(prop,value);
40
- }
41
- }
42
- });
43
- const attr_val=this.getAttribute(prop);
44
- if(attr_val!==null) this[prop]=attr_val;
45
- });
46
- this.pagination=this.getAttribute("pagination");
47
- this.searcher=this.hasAttribute("searcher");
48
- this.selectable=this.hasAttribute("selectable");
49
- this.multiple=this.hasAttribute("multiple");
50
- this.message=this.querySelector('artha-message')??this.querySelector(this.getAttribute('message-target'))??null;
32
+ this.message??=this.querySelector('artha-message')??this.querySelector(this.getAttribute('message-target'))??null;
33
+ this.id=this.getAttribute('id')??'container-'+BaseComponent.counter;
51
34
 
52
35
  // Loader
53
36
  this.loader_container=this._createLoader();
@@ -67,22 +50,7 @@ export default class ArthaContainer extends HTMLElement{
67
50
  }
68
51
 
69
52
  _createLoader(){
70
- return Util.createElement('div',(div)=>{
71
- div.classList.add('loader-container');
72
- div.setAttribute('title','Procesando...');
73
- div.appendChild(Util.createElement('div',(div2)=>{
74
- div2.classList.add('background-overlay');
75
- }));
76
- div.appendChild(Util.createElement('div',(div3)=>{
77
- div3.classList.add('loader','active');
78
- }));
79
- const img_container=div.appendChild(Util.createElement('div',(div4)=>{
80
- div4.classList.add('img-container');
81
- }));
82
- img_container.appendChild(Util.createElement('img',(img)=>{
83
- img.src='/assets/logo.png';
84
- }));
85
- });
53
+ return Util.createElement('artha-loader');
86
54
  }
87
55
 
88
56
  _handleSearch(evt){
@@ -139,8 +107,7 @@ export default class ArthaContainer extends HTMLElement{
139
107
  getData(search=null){
140
108
  if(!this.action) return;
141
109
  const query=search?{search}:{};
142
- const id=this.getAttribute("id")??Util.numberRandom(10000,99999);
143
- this.task_queue.loadTask(`container-${id}`,null,(task)=>{
110
+ this.task_queue.loadTask(`container-${this.id}`,null,(task)=>{
144
111
  XHR.request({
145
112
  url:this.action,
146
113
  method:this.method,
@@ -152,9 +119,9 @@ export default class ArthaContainer extends HTMLElement{
152
119
  onLoad:(xhr)=>{
153
120
  this.dispatchEvent(new CustomEvent('load',{detail:xhr}));
154
121
  },
155
- onData:(xhr,data)=>{
122
+ onData:(xhr,json)=>{
156
123
  // Respuesta procesada en en formato json
157
- task.resolve(xhr,(json)=>{
124
+ task.resolve(xhr,()=>{
158
125
  this.dispatchEvent(new CustomEvent('resolve',{detail:json}));
159
126
  if(json.message){
160
127
  this.message?.show(json.message,json.status);
@@ -206,9 +173,9 @@ export default class ArthaContainer extends HTMLElement{
206
173
  let index=0;
207
174
 
208
175
  for(const item of items){
209
- const wires=item.getAttribute("data-wire").split(",");
176
+ const wires=item.getAttribute("data-wire").split(":");
210
177
  for(let wire of wires){
211
- const [attrib_json,attrib_element,attrib_action]=wire.split(",");
178
+ const [attrib_json,attrib_element,attrib_action]=wire.split(":");
212
179
  let value=attrib_json?Util.getValueByPath(data,attrib_json.replaceAll("[]","")):data[index]??"";
213
180
  const append=attrib_action==="append";
214
181
  const chooser=attrib_action==="chooser";
@@ -1,9 +1,10 @@
1
+ import BaseComponent from '../abstract/BaseComponent.js';
1
2
  import Util from '../core/Util.js';
2
3
  import XHR from '../core/XHR.js';
3
4
  import TaskQueue from '../core/TaskQueue.js';
4
5
  import ArthaMessage from './artha-message.js';
5
6
 
6
- export default class ArthaForm extends HTMLElement{
7
+ export default class ArthaForm extends BaseComponent{
7
8
 
8
9
  constructor(){
9
10
  super();
@@ -46,6 +47,8 @@ export default class ArthaForm extends HTMLElement{
46
47
  default: btn.addEventListener('click',(evt)=>this.submit());
47
48
  }
48
49
  });
50
+ this.querySelector('[type="submit"]')?.addEventListener('click',(evt)=>this.submit());
51
+ this.querySelector('[type="reset"]')?.addEventListener('click',(evt)=>this.reset());
49
52
  }
50
53
 
51
54
  // Cargar inputs dinámicos
@@ -101,10 +104,14 @@ export default class ArthaForm extends HTMLElement{
101
104
 
102
105
  // Enviar formulario
103
106
  submit(){
107
+ if(!this.checkValidity()){
108
+ this.message.warning('Formulario incompleto');
109
+ return;
110
+ }
104
111
  const form_data={};
105
112
  this.element_inputs.forEach((element)=>{
106
113
  form_data[element.name]=element.type==='checkbox'?(element.checked?1:0):element.value;
107
- })
114
+ });
108
115
  const action=this.getAttribute('action')??'';
109
116
  const method=this.getAttribute('method')??'GET';
110
117
  const id=this.getAttribute('id');
@@ -117,9 +124,9 @@ export default class ArthaForm extends HTMLElement{
117
124
  onLoad:(xhr)=>{
118
125
  this.dispatchEvent(new CustomEvent('load',{detail:xhr}));
119
126
  },
120
- onData:(xhr,data)=>{
127
+ onData:(xhr,json)=>{
121
128
  // Respuesta procesada en formato json
122
- task.resolve(xhr,(json)=>{
129
+ task.resolve(xhr,()=>{
123
130
  this.dispatchEvent(new CustomEvent('resolve',{detail:json}));
124
131
  this.fillFromJson(json.data??{},false);
125
132
  });
@@ -0,0 +1,68 @@
1
+ import BaseComponent from '../abstract/BaseComponent.js';
2
+ import TaskQueue from '../core/TaskQueue.js';
3
+ import LoaderDots from '../components/loaders/LoaderDots.js';
4
+ import LoaderRing from '../components/loaders/LoaderRing.js';
5
+
6
+ export default class ArthaLoader extends BaseComponent{
7
+
8
+ static TYPE=Object.freeze({
9
+ DOTS:{
10
+ name:'dots',
11
+ clazz:LoaderDots
12
+ },
13
+ RING:{
14
+ name:'ring',
15
+ clazz:LoaderRing
16
+ },
17
+ BAR:{
18
+ name:'bar',
19
+ clazz:LoaderDots
20
+ },
21
+ WAVE:{
22
+ name:'wave',
23
+ clazz:LoaderDots
24
+ }
25
+ });
26
+
27
+ constructor(){
28
+ super(
29
+ ['type','text'],
30
+ {
31
+ defaults:{
32
+ type:ArthaLoader.TYPE.RING.name,
33
+ text:TaskQueue.defaults.title
34
+ },
35
+ resolvers:{
36
+ type:{
37
+ get:(value)=>{
38
+ return Object.values(ArthaLoader.TYPE).find((item)=>{
39
+ return item.name==value;
40
+ })?.clazz;
41
+ },
42
+ set:(value)=>{
43
+ return Object.values(ArthaLoader.TYPE).find((item)=>{
44
+ return item.clazz==value;
45
+ })?.name||value;
46
+ }
47
+ }
48
+ }
49
+ }
50
+ );
51
+ this.addEventListener('property-changed',()=>{
52
+ this.render();
53
+ });
54
+ this.render();
55
+ }
56
+
57
+ getLoaderInstance(){
58
+ return new (this.type)(this.getAttribute("type"),this.text);
59
+ }
60
+
61
+ render(){
62
+ this.innerHTML="";
63
+ const content=this.getLoaderInstance().render();
64
+ this.appendChild(content[0]);
65
+ this.appendChild(content[1]);
66
+ }
67
+
68
+ }
@@ -1,7 +1,7 @@
1
- import Config from "../core/Config.js";
1
+ import BaseComponent from '../abstract/BaseComponent.js';
2
2
  import Util from "../core/Util.js";
3
3
 
4
- export default class ArthaMessage extends HTMLElement{
4
+ export default class ArthaMessage extends BaseComponent{
5
5
 
6
6
  static TYPE=Object.freeze({
7
7
  ERROR:{
@@ -14,7 +14,7 @@ export default class ArthaMessage extends HTMLElement{
14
14
  },
15
15
  SUCCESS:{
16
16
  code:1,
17
- name:"suscess"
17
+ name:"success"
18
18
  },
19
19
  WARNING:{
20
20
  code:2,
@@ -25,10 +25,7 @@ export default class ArthaMessage extends HTMLElement{
25
25
 
26
26
  constructor(){
27
27
  super();
28
- }
29
-
30
- connectedCallback(){
31
- this.type=this.getAttribute("type")||"info";
28
+ this.type=this.getAttribute("type")||ArthaMessage.TYPE.INFO.name;
32
29
  this.hidden();
33
30
  }
34
31
 
@@ -49,7 +46,8 @@ export default class ArthaMessage extends HTMLElement{
49
46
  }
50
47
 
51
48
  show(message=null,type=null){
52
- if(type) this.setAttribute("type",(typeof type==="string"?type:type.name)||"info");
49
+ if(!message || type==null) return this.hidden();
50
+ if(type) this.setAttribute("type",(typeof type==="string"?type:type.name)||ArthaMessage.TYPE.INFO.name);
53
51
  if(message) this.innerHTML=message;
54
52
  Util.modal(this,true);
55
53
  }
@@ -0,0 +1,30 @@
1
+ import Util from "../../core/Util.js";
2
+
3
+ export default class LoaderBase{
4
+
5
+ constructor(type,text){
6
+ this.type=type;
7
+ this.text=text;
8
+ }
9
+
10
+ renderType(){
11
+ return Util.createElement('div',(div)=>{
12
+ div.classList.add('loader-content');
13
+ });
14
+ }
15
+
16
+ renderLoader=(loader)=>{
17
+ loader.appendChild(document.createElement('div'));
18
+ }
19
+
20
+ renderText(){
21
+ return Util.createElement('span',this.text);
22
+ }
23
+
24
+ render(){
25
+ let loader=this.renderType();
26
+ this.renderLoader(loader);
27
+ return [loader,this.renderText()];
28
+ }
29
+
30
+ }
@@ -0,0 +1,11 @@
1
+ import LoaderBase from './LoaderBase.js';
2
+
3
+ export default class LoaderDots extends LoaderBase{
4
+
5
+ renderLoader=(loader)=>{
6
+ loader.appendChild(document.createElement('div'));
7
+ loader.appendChild(document.createElement('div'));
8
+ loader.appendChild(document.createElement('div'));
9
+ };
10
+
11
+ }
@@ -0,0 +1,5 @@
1
+ import LoaderBase from './LoaderBase.js';
2
+
3
+ export default class LoaderRing extends LoaderBase{
4
+
5
+ }