@alexgyver/component 1.4.0 → 1.5.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/Component.js CHANGED
@@ -9,8 +9,19 @@ export class EL {
9
9
  * @param {Boolean} svg SVG
10
10
  */
11
11
  constructor(tag, data = {}, svg = false) {
12
- EL.context = this;
13
- this.$root = EL.make(tag, data, svg);
12
+ this.$root = EL.makeIn(this, tag, data, svg);
13
+ }
14
+ make(tag, data = {}, svg = false) {
15
+ return EL.makeIn(this, tag, data, svg);
16
+ }
17
+ makeArray(arr, svg = false) {
18
+ return EL.makeArrayIn(this, arr, svg);
19
+ }
20
+ config(el, data, svg = false) {
21
+ return EL.configIn(this, el, data, svg);
22
+ }
23
+ makeShadow(host, data = {}, sheet = null) {
24
+ return EL.makeShadowIn(this, host, data, sheet);
14
25
  }
15
26
 
16
27
  /**
@@ -19,34 +30,14 @@ export class EL {
19
30
  * @param {object} data параметры
20
31
  * @param {Boolean} svg SVG
21
32
  * @returns {Node}
22
- * @params
23
- * tag {string} - тег html элемента. Если 'svg' - включится режим SVG на детей
24
- * context {object} - объект для параметра '$', прокидывается в детей
25
- * parent - {Element} добавить созданный элемент к указанному элементу
26
- * text {string} - добавить в textContent
27
- * html {string} - добавить в innerHTML
28
- * class {string | Array} - добавить в className
29
- * style/style_r {string | object} - объект в виде { padding: '0px', ... } или строка css стилей. _r - заменить имеющиеся
30
- * push {array} - добавить к указанному массиву
31
- * $ {string} - создать переменную $имя в указанном объекте
32
- * events {object} - добавить addEventListener'ы {event: e => {}}
33
- * click, change, input, mousewheel - добавление события без events
34
- * children/children_r - дети, массив {DOM, EL, object, html string}. _r - заменить имеющиеся. Без тега tag будет div
35
- * child/child_r - ребенок {DOM, EL, object, html string}. _r - заменить имеющиеся. Без тега tag будет div
36
- * attrs {object} - добавить аттрибуты (как setAttribute())
37
- * props {object} - добавить свойства (как el[prop])
38
- * also {function} - вызвать с текущим элементом: also(el) { console.log(el); }
39
- * всё остальное будет добавлено как property
40
33
  */
41
34
  static make(tag, data = {}, svg = false) {
42
- if (!tag || typeof data !== 'object') return null;
43
35
  if (data instanceof Node) return data;
44
36
  if (tag == 'svg') svg = true;
45
- return EL.config(svg ? document.createElementNS("http://www.w3.org/2000/svg", tag) : document.createElement(tag), data, svg);
37
+ return EL.config(svg ? document.createElementNS("http://www.w3.org/2000/svg", tag ?? 'svg') : document.createElement(tag ?? 'div'), data, svg);
46
38
  }
47
39
  static makeIn(ctx, tag, data = {}, svg = false) {
48
- data.context = ctx;
49
- return EL.make(tag, data, svg);
40
+ return EL.make(tag, { ...data, context: ctx }, svg);
50
41
  }
51
42
 
52
43
  /**
@@ -54,77 +45,186 @@ export class EL {
54
45
  * @param {Node | Array} el элемент или массив элементов
55
46
  * @param {object} data параметры
56
47
  * @param {Boolean} svg SVG
57
- * @returns {Node}
48
+ * @returns {Node | Array}
58
49
  */
59
50
  static config(el, data, svg = false) {
51
+ if (!(el instanceof Node) || (typeof data !== 'object')) {
52
+ return el;
53
+ }
60
54
  if (Array.isArray(el)) {
61
- el.forEach(e => EL.config(e, data, svg));
62
- return null;
55
+ return el.map(e => EL.config(e, data, svg));
63
56
  }
64
- if (!(el instanceof Node) || (typeof data !== 'object')) return el;
65
-
66
- let ctx = data.context;
67
- EL.context = (ctx === null) ? null : (ctx ? ctx : EL.context);
68
- ctx = EL.context;
69
-
70
- let addChild = (obj) => {
71
- if (!obj) return;
72
- if (obj instanceof Node) el.appendChild(obj);
73
- else if (obj instanceof EL) el.appendChild(obj.$root);
74
- else if (typeof obj === 'string') el.innerHTML += obj;
75
- else if (typeof obj === 'object') {
76
- let cmp = EL.make(obj.tag ?? 'div', obj, svg || obj.tag == 'svg');
77
- if (cmp) el.appendChild(cmp);
57
+
58
+ const ctx = ('context' in data) ? (EL.context = data.context) : EL.context;
59
+
60
+ const addChild = obj => {
61
+ if (obj) {
62
+ if (obj instanceof Node) el.appendChild(obj);
63
+ else if (obj instanceof EL) el.appendChild(obj.$root);
64
+ else if (typeof obj === 'string') el.insertAdjacentHTML('beforeend', obj);
65
+ else if (typeof obj === 'object') EL.make(obj.tag, { ...obj, parent: el }, (svg || obj.tag == 'svg'));
78
66
  }
79
67
  }
80
68
 
69
+ const call = fn => { if (fn) fn.call(ctx, el, ctx) }
70
+
81
71
  for (const [key, val] of Object.entries(data)) {
82
72
  switch (key) {
83
- case 'text': el.textContent = (val == null) ? '' : (val + ''); continue;
84
- case 'html': el.innerHTML = (val == null) ? '' : (val + ''); continue;
73
+ case 'text':
74
+ el.textContent = (val == null) ? '' : String(val); // == - null + undef
75
+ continue;
76
+
77
+ case 'html':
78
+ el.innerHTML = (val == null) ? '' : String(val);
79
+ continue;
80
+
85
81
  case 'tag':
86
- case 'context':
87
82
  case 'get':
88
83
  case 'also':
84
+ case 'parent':
85
+ case 'context':
86
+ case 'onMount':
87
+ case 'onUpdate':
88
+ case 'onDestroy':
89
89
  continue;
90
90
  }
91
- if (!val) continue;
91
+
92
+ if (val === undefined || val === null) continue;
92
93
 
93
94
  switch (key) {
94
- case 'class': (Array.isArray(val) ? val : val.split(' ')).map(c => c && el.classList.add(c)); break;
95
- case 'push': val.push(el); break;
96
- case '$': case 'var': if (ctx) ctx['$' + val] = el; break;
97
- case 'events': for (let ev in val) el.addEventListener(ev, ctx ? val[ev].bind(ctx) : val[ev]); break;
98
- case 'click': case 'change': case 'input': case 'mousewheel': el.addEventListener(key, ctx ? val.bind(ctx) : val); break;
99
- case 'parent': val.appendChild(el); break;
100
- case 'attrs': for (let attr in val) el.setAttribute(attr, val[attr]); break;
101
- case 'props': for (let prop in val) el[prop] = val[prop]; break;
102
- case 'child_r': EL.clear(el); // fall
103
- case 'child': addChild(val); break;
104
- case 'children_r': EL.clear(el); // fall
105
- case 'children': for (let obj of val) addChild(obj); break;
106
- case 'style_r': el.style.cssText = ''; // fall
95
+ case 'push':
96
+ val.push(el);
97
+ break;
98
+
99
+ case '$':
100
+ case 'var':
101
+ if (ctx) ctx['$' + val] = el;
102
+ break;
103
+
104
+ case 'events':
105
+ for (let ev in val) el.addEventListener(ev, e => val[ev].call(ctx, e, el, ctx));
106
+ break;
107
+
108
+ case 'click':
109
+ case 'input':
110
+ case 'change':
111
+ case 'mousewheel':
112
+ el.addEventListener(key, e => val.call(ctx, e, el, ctx));
113
+ break;
114
+
115
+ case 'attrs':
116
+ for (let attr in val) el.setAttribute(attr, val[attr]);
117
+ break;
118
+
119
+ case 'props':
120
+ for (let prop in val) el[prop] = val[prop];
121
+ break;
122
+
123
+ case 'data':
124
+ for (let key in val) el.dataset[key] = val[key];
125
+ break;
126
+
127
+ case 'child_r':
128
+ EL.clear(el);
129
+ // fall
130
+ case 'child':
131
+ addChild(val);
132
+ break;
133
+
134
+ case 'children_r':
135
+ EL.clear(el);
136
+ // fall
137
+ case 'children':
138
+ for (let obj of val) addChild(obj);
139
+ break;
140
+
141
+ case 'style_r':
142
+ el.style.cssText = '';
143
+ // fall
107
144
  case 'style':
108
- if (typeof val === 'string') el.style.cssText += val + ';';
109
- else for (let st in val) if (val[st]) el.style[st] = val[st];
145
+ if (typeof val === 'string') {
146
+ el.style.cssText += val + ';';
147
+ } else {
148
+ for (let st in val) if (val[st]) el.style[st] = val[st];
149
+ }
150
+ break;
151
+
152
+ case 'class_r':
153
+ el.className = '';
154
+ // fall
155
+ case 'class': {
156
+ const getClasses = (cls) => {
157
+ if (Array.isArray(cls)) return Object.fromEntries(cls.filter(Boolean).map(c => [c, true]));
158
+ if (typeof cls === 'string') return getClasses(cls.split(/\s+/));
159
+ return cls;
160
+ }
161
+ for (const [cls, state] of Object.entries(getClasses(val))) {
162
+ state ? el.classList.add(cls) : el.classList.remove(cls);
163
+ }
164
+ } break;
165
+
166
+ case 'animate': {
167
+ const { duration = 300, easing = 'ease', onEnd = null, ...styles } = val;
168
+ el.style.transition = Object.keys(styles).map(st => `${st} ${duration}ms ${easing}`).join(', ');
169
+ requestAnimationFrame(() => { for (let st in styles) el.style[st] = styles[st]; });
170
+
171
+ const onEndHandler = () => {
172
+ el.removeEventListener('transitionend', onEndHandler);
173
+ call(onEnd);
174
+ };
175
+ el.addEventListener('transitionend', onEndHandler);
176
+ } break;
177
+
178
+ default: el[key] = val;
110
179
  break;
111
- default: el[key] = val; break;
112
180
  }
113
181
  }
114
- if (data.also && ctx) data.also.call(ctx, el);
182
+
183
+ if (data.parent) data.parent.appendChild(el);
184
+
185
+ let tries = 50;
186
+ const mount = () => {
187
+ if (!el._mounted) {
188
+ if (el.isConnected) {
189
+ el._mounted = true;
190
+ call(data.onMount);
191
+ } else if (--tries) {
192
+ requestAnimationFrame(mount);
193
+ }
194
+ }
195
+ }
196
+ mount();
197
+
198
+ if (data.onDestroy) el._onDestroy = data.onDestroy.bind(ctx, el, ctx);
199
+ if (data.onUpdate) el._onUpdate = data.onUpdate.bind(ctx, el, ctx);
200
+ if (el._onUpdate) el._onUpdate();
201
+ call(data.also);
202
+
115
203
  return el;
116
204
  }
117
205
  static configIn(ctx, el, data, svg = false) {
118
- data.context = ctx;
119
- return EL.config(el, data, svg);
206
+ return EL.config(el, { ...data, context: ctx }, svg);
120
207
  }
121
208
 
122
209
  /**
123
210
  * Удалить все child ноды
124
211
  * @param {HTMLElement} el
125
212
  */
126
- static clear(el) {
127
- while (el.firstChild) el.removeChild(el.lastChild);
213
+ static clear(el, recursive = true) {
214
+ while (el.firstChild) {
215
+ if (recursive) EL.clear(el.firstChild, true);
216
+ EL.remove(el.firstChild, false);
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Удалить элемент
222
+ * @param {HTMLElement} el
223
+ */
224
+ static remove(el, recursive = true) {
225
+ if (recursive) EL.clear(el);
226
+ if (el._onDestroy) el._onDestroy();
227
+ el.remove();
128
228
  }
129
229
 
130
230
  /**
@@ -135,7 +235,10 @@ export class EL {
135
235
  */
136
236
  static makeArray(arr, svg = false) {
137
237
  if (!arr || !Array.isArray(arr)) return [];
138
- return arr.map(x => EL.make(x.tag, x, svg));
238
+ return arr.map(data => EL.make(data.tag, data, svg));
239
+ }
240
+ static makeArrayIn(ctx, arr, svg) {
241
+ return EL.makeArray(arr.map(data => ({ ...data, context: ctx })), svg);
139
242
  }
140
243
 
141
244
  /**
@@ -167,11 +270,33 @@ export class EL {
167
270
  EL.config($host, data);
168
271
  return $host;
169
272
  }
273
+ static makeShadowIn(ctx, host, data = {}, sheet = null) {
274
+ return EL.makeShadow(host, { ...data, context: ctx }, sheet);
275
+ }
170
276
  }
171
277
 
172
278
  // legacy
173
279
  export const Component = EL;
174
280
 
281
+ //#region State
282
+ export class State {
283
+ constructor(init = {}) {
284
+ this.data = init;
285
+ this.subs = new Set();
286
+ }
287
+ set(key, value) {
288
+ this.data[key] = value;
289
+ this.subs.forEach(fn => fn(this.data));
290
+ }
291
+ get(key) {
292
+ return this.data[key];
293
+ }
294
+ subscribe(fn) {
295
+ this.subs.add(fn);
296
+ return () => this.subs.delete(fn);
297
+ }
298
+ }
299
+
175
300
  //#region SVG
176
301
  export class SVG {
177
302
  static make = (tag, data) => EL.make(tag, data, true);
package/Component.min.js CHANGED
@@ -1 +1 @@
1
- var e={d:(t,a)=>{for(var c in a)e.o(a,c)&&!e.o(t,c)&&Object.defineProperty(t,c,{enumerable:!0,get:a[c]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{EL:()=>a,LV:()=>r,cj:()=>s,t4:()=>n,uA:()=>c});class a{static context;constructor(e,t={},c=!1){a.context=this,this.$root=a.make(e,t,c)}static make(e,t={},c=!1){return e&&"object"==typeof t?t instanceof Node?t:("svg"==e&&(c=!0),a.config(c?document.createElementNS("http://www.w3.org/2000/svg",e):document.createElement(e),t,c)):null}static makeIn(e,t,c={},n=!1){return c.context=e,a.make(t,c,n)}static config(e,t,c=!1){if(Array.isArray(e))return e.forEach(e=>a.config(e,t,c)),null;if(!(e instanceof Node)||"object"!=typeof t)return e;let n=t.context;a.context=null===n?null:n||a.context,n=a.context;let s=t=>{if(t)if(t instanceof Node)e.appendChild(t);else if(t instanceof a)e.appendChild(t.$root);else if("string"==typeof t)e.innerHTML+=t;else if("object"==typeof t){let n=a.make(t.tag??"div",t,c||"svg"==t.tag);n&&e.appendChild(n)}};for(const[c,r]of Object.entries(t)){switch(c){case"text":e.textContent=null==r?"":r+"";continue;case"html":e.innerHTML=null==r?"":r+"";continue;case"tag":case"context":case"get":case"also":continue}if(r)switch(c){case"class":(Array.isArray(r)?r:r.split(" ")).map(t=>t&&e.classList.add(t));break;case"push":r.push(e);break;case"$":case"var":n&&(n["$"+r]=e);break;case"events":for(let t in r)e.addEventListener(t,n?r[t].bind(n):r[t]);break;case"click":case"change":case"input":case"mousewheel":e.addEventListener(c,n?r.bind(n):r);break;case"parent":r.appendChild(e);break;case"attrs":for(let t in r)e.setAttribute(t,r[t]);break;case"props":for(let t in r)e[t]=r[t];break;case"child_r":a.clear(e);case"child":s(r);break;case"children_r":a.clear(e);case"children":for(let e of r)s(e);break;case"style_r":e.style.cssText="";case"style":if("string"==typeof r)e.style.cssText+=r+";";else for(let t in r)r[t]&&(e.style[t]=r[t]);break;default:e[c]=r}}return t.also&&n&&t.also.call(n,e),e}static configIn(e,t,c,n=!1){return c.context=e,a.config(t,c,n)}static clear(e){for(;e.firstChild;)e.removeChild(e.lastChild)}static makeArray(e,t=!1){return e&&Array.isArray(e)?e.map(e=>a.make(e.tag,e,t)):[]}static makeShadow(e,t={},c=null){if(!e||"object"!=typeof t)return null;let n=e instanceof Node?e:document.createElement(e);return n.attachShadow({mode:"open"}),a.config(n.shadowRoot,{context:t.context,children:[{tag:"style",textContent:c??""},t.child??{},...t.children??[]]}),delete t.children,delete t.child,a.config(n,t),n}}const c=a;class n{static make=(e,t)=>a.make(e,t,!0);static config=(e,t)=>a.config(e,t,!0);static makeArray=e=>a.makeArray(e,!0);static svg=(e={},t={})=>n._make("svg",e,t);static rect=(e,t,a,c,s,r,o={},i={})=>n._make("rect",{...o,x:e,y:t,width:a,height:c,rx:s,ry:r},i);static circle=(e,t,a,c={},s={})=>n._make("circle",{...c,cx:e,cy:t,r:a},s);static line=(e,t,a,c,s={},r={})=>n._make("line",{...s,x1:e,y1:t,x2:a,y2:c},r);static polyline=(e,t={},a={})=>n._make("polyline",{...t,points:e},a);static polygon=(e,t={},a={})=>n._make("polygon",{...t,points:e},a);static path=(e,t={},a={})=>n._make("path",{...t,d:e},a);static text=(e,t,a,c={},s={})=>n._make("text",{...c,x:t,y:a},{...s,text:e});static _make=(e,t={},a={})=>n.make(e,{attrs:{...t},...a})}class s{static addStyle(e,t,a=!1){if(e&&t&&("object"==typeof t&&(t=t.constructor.name),!s.#e.has(t)&&!s.#t.has(t)))if(a){let a=document.createElement("style");document.head.appendChild(a),a.textContent=e,s.#t.set(t,a)}else s.#a||(s.#a=document.head.appendChild(document.createElement("style"))),s.#a.textContent+=e+"\r\n",s.#e.add(t)}static removeStyle(e){"object"==typeof e&&(e=e.constructor.name),s.#t.has(e)&&(s.#t.get(e).remove(),s.#t.delete(e))}static#a=null;static#e=new Set;static#t=new Map}class r extends a{constructor(e,t={},a=null,c=null,n=!1){super(e,t),s.addStyle(a,c,n)}static make(e,t={},c=null,n=null,r=!1){return s.addStyle(c,n,r),a.make(e,t)}}const o=t.uA,i=t.EL,l=t.t4,d=t.cj,h=t.LV;export{o as Component,i as EL,l as SVG,d as Sheet,h as StyledComponent};
1
+ var e={d:(t,a)=>{for(var n in a)e.o(a,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:a[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{EL:()=>a,LV:()=>o,Uw:()=>s,cj:()=>c,t4:()=>r,uA:()=>n});class a{static context;constructor(e,t={},n=!1){this.$root=a.makeIn(this,e,t,n)}make(e,t={},n=!1){return a.makeIn(this,e,t,n)}makeArray(e,t=!1){return a.makeArrayIn(this,e,t)}config(e,t,n=!1){return a.configIn(this,e,t,n)}makeShadow(e,t={},n=null){return a.makeShadowIn(this,e,t,n)}static make(e,t={},n=!1){return t instanceof Node?t:("svg"==e&&(n=!0),a.config(n?document.createElementNS("http://www.w3.org/2000/svg",e??"svg"):document.createElement(e??"div"),t,n))}static makeIn(e,t,n={},s=!1){return a.make(t,{...n,context:e},s)}static config(e,t,n=!1){if(!(e instanceof Node)||"object"!=typeof t)return e;if(Array.isArray(e))return e.map(e=>a.config(e,t,n));const s="context"in t?a.context=t.context:a.context,r=t=>{t&&(t instanceof Node?e.appendChild(t):t instanceof a?e.appendChild(t.$root):"string"==typeof t?e.insertAdjacentHTML("beforeend",t):"object"==typeof t&&a.make(t.tag,{...t,parent:e},n||"svg"==t.tag))},c=t=>{t&&t.call(s,e,s)};for(const[n,o]of Object.entries(t)){switch(n){case"text":e.textContent=null==o?"":String(o);continue;case"html":e.innerHTML=null==o?"":String(o);continue;case"tag":case"get":case"also":case"parent":case"context":case"onMount":case"onUpdate":case"onDestroy":continue}if(null!=o)switch(n){case"push":o.push(e);break;case"$":case"var":s&&(s["$"+o]=e);break;case"events":for(let t in o)e.addEventListener(t,a=>o[t].call(s,a,e,s));break;case"click":case"input":case"change":case"mousewheel":e.addEventListener(n,t=>o.call(s,t,e,s));break;case"attrs":for(let t in o)e.setAttribute(t,o[t]);break;case"props":for(let t in o)e[t]=o[t];break;case"data":for(let t in o)e.dataset[t]=o[t];break;case"child_r":a.clear(e);case"child":r(o);break;case"children_r":a.clear(e);case"children":for(let e of o)r(e);break;case"style_r":e.style.cssText="";case"style":if("string"==typeof o)e.style.cssText+=o+";";else for(let t in o)o[t]&&(e.style[t]=o[t]);break;case"class_r":e.className="";case"class":{const t=e=>Array.isArray(e)?Object.fromEntries(e.filter(Boolean).map(e=>[e,!0])):"string"==typeof e?t(e.split(/\s+/)):e;for(const[a,n]of Object.entries(t(o)))n?e.classList.add(a):e.classList.remove(a)}break;case"animate":{const{duration:t=300,easing:a="ease",onEnd:n=null,...s}=o;e.style.transition=Object.keys(s).map(e=>`${e} ${t}ms ${a}`).join(", "),requestAnimationFrame(()=>{for(let t in s)e.style[t]=s[t]});const r=()=>{e.removeEventListener("transitionend",r),c(n)};e.addEventListener("transitionend",r)}break;default:e[n]=o}}t.parent&&t.parent.appendChild(e);let o=50;const i=()=>{e._mounted||(e.isConnected?(e._mounted=!0,c(t.onMount)):--o&&requestAnimationFrame(i))};return i(),t.onDestroy&&(e._onDestroy=t.onDestroy.bind(s,e,s)),t.onUpdate&&(e._onUpdate=t.onUpdate.bind(s,e,s)),e._onUpdate&&e._onUpdate(),c(t.also),e}static configIn(e,t,n,s=!1){return a.config(t,{...n,context:e},s)}static clear(e,t=!0){for(;e.firstChild;)t&&a.clear(e.firstChild,!0),a.remove(e.firstChild,!1)}static remove(e,t=!0){t&&a.clear(e),e._onDestroy&&e._onDestroy(),e.remove()}static makeArray(e,t=!1){return e&&Array.isArray(e)?e.map(e=>a.make(e.tag,e,t)):[]}static makeArrayIn(e,t,n){return a.makeArray(t.map(t=>({...t,context:e})),n)}static makeShadow(e,t={},n=null){if(!e||"object"!=typeof t)return null;let s=e instanceof Node?e:document.createElement(e);return s.attachShadow({mode:"open"}),a.config(s.shadowRoot,{context:t.context,children:[{tag:"style",textContent:n??""},t.child??{},...t.children??[]]}),delete t.children,delete t.child,a.config(s,t),s}static makeShadowIn(e,t,n={},s=null){return a.makeShadow(t,{...n,context:e},s)}}const n=a;class s{constructor(e={}){this.data=e,this.subs=new Set}set(e,t){this.data[e]=t,this.subs.forEach(e=>e(this.data))}get(e){return this.data[e]}subscribe(e){return this.subs.add(e),()=>this.subs.delete(e)}}class r{static make=(e,t)=>a.make(e,t,!0);static config=(e,t)=>a.config(e,t,!0);static makeArray=e=>a.makeArray(e,!0);static svg=(e={},t={})=>r._make("svg",e,t);static rect=(e,t,a,n,s,c,o={},i={})=>r._make("rect",{...o,x:e,y:t,width:a,height:n,rx:s,ry:c},i);static circle=(e,t,a,n={},s={})=>r._make("circle",{...n,cx:e,cy:t,r:a},s);static line=(e,t,a,n,s={},c={})=>r._make("line",{...s,x1:e,y1:t,x2:a,y2:n},c);static polyline=(e,t={},a={})=>r._make("polyline",{...t,points:e},a);static polygon=(e,t={},a={})=>r._make("polygon",{...t,points:e},a);static path=(e,t={},a={})=>r._make("path",{...t,d:e},a);static text=(e,t,a,n={},s={})=>r._make("text",{...n,x:t,y:a},{...s,text:e});static _make=(e,t={},a={})=>r.make(e,{attrs:{...t},...a})}class c{static addStyle(e,t,a=!1){if(e&&t&&("object"==typeof t&&(t=t.constructor.name),!c.#e.has(t)&&!c.#t.has(t)))if(a){let a=document.createElement("style");document.head.appendChild(a),a.textContent=e,c.#t.set(t,a)}else c.#a||(c.#a=document.head.appendChild(document.createElement("style"))),c.#a.textContent+=e+"\r\n",c.#e.add(t)}static removeStyle(e){"object"==typeof e&&(e=e.constructor.name),c.#t.has(e)&&(c.#t.get(e).remove(),c.#t.delete(e))}static#a=null;static#e=new Set;static#t=new Map}class o extends a{constructor(e,t={},a=null,n=null,s=!1){super(e,t),c.addStyle(a,n,s)}static make(e,t={},n=null,s=null,r=!1){return c.addStyle(n,s,r),a.make(e,t)}}const i=t.uA,l=t.EL,d=t.t4,m=t.cj,u=t.Uw,h=t.LV;export{i as Component,l as EL,d as SVG,m as Sheet,u as State,h as StyledComponent};
package/README.md CHANGED
@@ -4,47 +4,63 @@
4
4
  > npm i @alexgyver/component
5
5
 
6
6
  ## Дока
7
- ### Component
7
+ ### EL
8
+ Параметры для конфига `data`:
9
+
10
+ - `context` [объект] - привязывает контекст для других параметров (см ниже), пробрасывается в детей и не сбрасывается между вызовами make/config
11
+ - `$` [текст] - добавляет созданный элемент в context с именем `$значение`
12
+ - `events` [объект] - подключает события `{ eventName: handlerFunc(event, element, context) }`, в this тоже прокидывается context
13
+ - `click`, `change`, `input`, `mousewheel` [функция] - подключает эти события как `handlerFunc(event, element, context)`, в this тоже прокидывается context
14
+ - `also` [функция] - вызывает указанную функцию вида `handlerFunc(element, context)`, в this тоже прокидывается context
15
+ - `text` [текст] - добавится в textContent, nullish значения - очистить
16
+ - `html` [текст] - добавится в innerHTML, nullish значения - очистить
17
+ - `tag` [текст] - HTML тег для child-объектов
18
+ - `push` [массив] - добавляет созданный элемент в указанный массив
19
+ - `parent` [Node] - добавит к нему созданный элемент как child
20
+ - `attrs` [объект] - аттрибуты будут установлены через setAttribute
21
+ - `props` [объект] - свойства будут установлены как el[prop] = val
22
+ - `data` [объект] - датасеты будут добавлены как аттрибуты data-name = value
23
+ - `child`, `child_r` [объект] конфиг, добавится как ребёнок к созданному элементу. Без указания tag будет добавлен div. `_r` - заменить ребёнка
24
+ - `children`, `children_r` [массив] объектов конфига, добавятся как дети к созданному элементу. Без указания tag будет добавлен div. `_r` - заменить всех детей на новых
25
+ - `animate` [объект] - добавить CSS анимаций, может содержать параметры анимации `duration` и `easing`, обработчик окончания анимации `onEnd(element, context)`, в this тоже прокидывается context
26
+ - `style`, `style_r` - стили. Принимает:
27
+ - [Строка] CSS стилей
28
+ - [Объект] вида `{styleName: value}`
29
+ - `class`, `class_r` - установка классов в classList, версия с `_r` - заменить классы. Принимает:
30
+ - [Строку] вида `newClass active foo bar`
31
+ - [Массив] вида `['newClass', 'active']`, причём можно по условию: `['newClass', isActive && 'active']`
32
+ - [Объект] вида `{newClass: true, active: false}` - true значение добавляет класс, false не добавляет
33
+ - `onUpdate` [функция] вида `handlerFunc(element, context)`, вызовется при настройке через функцию config, в this тоже прокидывается context
34
+ - `onMount` [функция] вида `handlerFunc(element, context)`, вызовется при присоединении к DOM, в this тоже прокидывается context
35
+ - `onDestroy` [функция] вида `handlerFunc(element, context)`, вызовется при удалении через EL.remove(el) или EL.clear(el), в this тоже прокидывается context
36
+ - Другие значения будут добавлены как свойства
37
+
38
+ #### Класс
8
39
  ```js
9
40
  /**
10
- * Создать компонент и поместить его в переменную $root
41
+ * Создать компонент и поместить его в переменную this.$root
11
42
  * @param {string} tag html tag элемента
12
43
  * @param {object} data параметры
13
44
  */
14
45
  EL(tag, data = {}, svg = false);
15
46
 
47
+ // аналоги с авто-this контекстом для вызова внутри класса
48
+ make(tag, data = {}, svg = false);
49
+ makeArray(arr, svg = false);
50
+ config(el, data, svg = false);
51
+ makeShadow(host, data = {}, sheet = null);
52
+ ```
53
+
54
+ #### Static
55
+ ```js
16
56
  /**
17
57
  * Создать компонент
18
58
  * @param {string} tag html tag элемента
19
59
  * @param {object} data параметры
20
60
  * @returns {Node}
21
- * tag {string} - тег html элемента. Если 'svg' - включится режим SVG на детей
22
- * context {object} - контекст для параметра 'var' и вызовов, прокидывается в детей. Если указан явно как null - прерывает проброс
23
- * parent - {Element} добавляет компонент к указанному элементу
24
- * text {string} - добавить в textContent
25
- * html {string} - добавить в innerHTML
26
- * class {string | Array} - добавить в className
27
- * style {string | object} - объект в виде { padding: '0px', ... } или строка css стилей
28
- * push {array} - добавить к указанному массиву
29
- * var | $ {string} - создаёт переменную $имя в указанном контексте
30
- * events {object} - добавляет addEventListener'ы {event: e => {}}
31
- * children/children_r - массив {DOM, EL, object, html string}. _r - заменить имеющиеся. Без тега tag будет div
32
- * child/child_r - {DOM, EL, object, html string}. _r - заменить имеющиеся. Без тега tag будет div
33
- * attrs {object} - добавить аттрибуты (через setAttribute)
34
- * props {object} - добавить свойства (как el[prop])
35
- * also {function} - вызвать с текущим компонентом: also(el) { console.log(el); }
36
- * всё остальное будет добавлено как property
37
61
  */
38
62
  EL.make(tag, data = {});
39
-
40
- /**
41
- * Создать теневой компонент от указанного tag, дети подключатся к нему в shadowRoot
42
- * @param {string|Node} host html tag теневого элемента или Node
43
- * @param {object} data параметры внешнего элемента
44
- * @param {string} sheet css стили
45
- * @returns {Node} host
46
- */
47
- EL.makeShadow(host, data = {}, sheet = null);
63
+ EL.makeIn(ctx, tag, data = {}, svg = false);
48
64
 
49
65
  /**
50
66
  * Настроить элемент
@@ -53,6 +69,7 @@ EL.makeShadow(host, data = {}, sheet = null);
53
69
  * @returns {Node}
54
70
  */
55
71
  EL.config(el, data);
72
+ EL.configIn(ctx, el, data, svg = false);
56
73
 
57
74
  /**
58
75
  * Создать массив компонентов из массива объектов конфигурации
@@ -60,6 +77,29 @@ EL.config(el, data);
60
77
  * @returns {array} of Elements
61
78
  */
62
79
  EL.makeArray(arr);
80
+ EL.makeArrayIn(ctx, arr, svg);
81
+
82
+ /**
83
+ * Создать теневой компонент от указанного tag, дети подключатся к нему в shadowRoot
84
+ * @param {string|Node} host html tag теневого элемента или Node
85
+ * @param {object} data параметры внешнего элемента
86
+ * @param {string} sheet css стили
87
+ * @returns {Node} host
88
+ */
89
+ EL.makeShadow(host, data = {}, sheet = null);
90
+ EL.makeShadowIn(ctx, host, data = {}, sheet = null);
91
+
92
+ /**
93
+ * Удалить все child ноды
94
+ * @param {HTMLElement} el
95
+ */
96
+ EL.clear(el, recursive = true);
97
+
98
+ /**
99
+ * Удалить элемент
100
+ * @param {HTMLElement} el
101
+ */
102
+ EL.remove(el, recursive = true);
63
103
  ```
64
104
 
65
105
  ### SVG
@@ -118,8 +158,9 @@ StyledComponent(tag, data = {}, style = null, id = null, ext = false);
118
158
  StyledComponent.make(tag, data = {}, style = null, id = null, ext = false);
119
159
  ```
120
160
 
121
- ## Пример
161
+ ## Примеры
122
162
  Создаст контейнер с двумя вложенными блоками текста и прикрепит к body
163
+
123
164
  ```js
124
165
  EL.make('div', {
125
166
  parent: document.body,
@@ -143,6 +184,46 @@ EL.make('div', {
143
184
  });
144
185
  ```
145
186
 
187
+ Простой элемент с текстом и классами
188
+
189
+ ```js
190
+ const box = new EL('div', {
191
+ text: 'Привет, мир!',
192
+ class: ['box', 'highlight'],
193
+ style: { padding: '10px', color: 'white', backgroundColor: 'blue' },
194
+ click() { console.log('Box кликнут!'); },
195
+ $: 'myBox' // создаёт ctx.$myBox
196
+ });
197
+
198
+ document.body.appendChild(box.$root);
199
+ console.log(box.$root); // сам DOM-элемент
200
+ console.log(EL.context.$myBox); // доступ через контекст
201
+ ```
202
+
203
+ Полная замена классов и стилей
204
+
205
+ ```js
206
+ EL.config(someElement, {
207
+ text: 'Обновлённый блок',
208
+ class_r: ['newClass', 'active'], // полностью заменяет классы
209
+ style_r: { color: 'red', fontWeight: 'bold' } // сброс и новые стили
210
+ });
211
+ ```
212
+
213
+ Вложенные дети и массив children
214
+
215
+ ```cpp
216
+ EL.make('div', {
217
+ parent: document.body,
218
+ class: 'parent',
219
+ children: [
220
+ { tag: 'p', text: 'Первый ребёнок', class: 'child' },
221
+ { tag: 'p', text: 'Второй ребёнок', class: ['child', 'second'] },
222
+ 'Просто текстовый узел'
223
+ ]
224
+ });
225
+ ```
226
+
146
227
  Гораздо интереснее использовать в классе и передавать контекст. Параметр `$` создаст переменную с элементом с указанным именем + префикс `$`:
147
228
 
148
229
  ```js
@@ -166,6 +247,25 @@ let btn = new Button('kek');
166
247
  btn.$button; // элемент кнопки
167
248
  ```
168
249
 
250
+ Использование контекста для $ и событий
251
+
252
+ ```js
253
+ class MyComponent extends EL {
254
+ constructor() {
255
+ super('div', {
256
+ class: 'comp',
257
+ $: 'root',
258
+ children: [
259
+ { tag: 'button', text: 'Нажми меня', click() { console.log(this.$root); } }
260
+ ]
261
+ });
262
+ }
263
+ }
264
+
265
+ const comp = new MyComponent();
266
+ document.body.appendChild(comp.$root);
267
+ ```
268
+
169
269
  Некоторые трюки
170
270
 
171
271
  ```js
@@ -194,3 +294,47 @@ EL.make('div', {
194
294
  class: ['some', 'class', foo && 'plus_me'], // добавить plus_me если foo - true
195
295
  });
196
296
  ```
297
+
298
+
299
+ Создание SVG
300
+
301
+ ```js
302
+ const svg = SVG.svg({ width: 200, height: 200 }, {
303
+ children: [
304
+ SVG.rect(10, 10, 50, 50, 5, 5, { fill: 'green' }),
305
+ SVG.circle(100, 100, 40, { fill: 'red' }),
306
+ SVG.line(0, 0, 200, 200, { stroke: 'blue', 'stroke-width': 2 })
307
+ ]
308
+ });
309
+ ```
310
+
311
+ Shadow DOM со стилями
312
+
313
+ ```js
314
+ const shadowHost = EL.makeShadow('div', {
315
+ child: { tag: 'p', text: 'Текст внутри Shadow DOM', class: 'shadow-text' }
316
+ }, `
317
+ .shadow-text { color: purple; font-weight: bold; }
318
+ `);
319
+
320
+ document.body.appendChild(shadowHost);
321
+ ```
322
+
323
+
324
+ Прочее
325
+
326
+ ```js
327
+ const state = new State({ count: 0 });
328
+
329
+ let d = EL.make('div', {
330
+ text: state.get('count'),
331
+ also: el => state.subscribe(d => el.textContent = d.count),
332
+ style: { width: '100px', height: '50px', background: 'red' },
333
+ animate: { width: '200px', background: 'blue', duration: 500 },
334
+ onUpdate: el => console.log('update'),
335
+ onMount: el => console.log('mount'),
336
+ });
337
+
338
+ setInterval(() => state.set('count', state.get('count') + 1), 1000);
339
+ setTimeout(() => { document.body.appendChild(d) }, 2000);
340
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexgyver/component",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Simple HTML&SVG element builder",
5
5
  "main": "./Component.js",
6
6
  "module": "./Component.js",
@@ -15,7 +15,7 @@
15
15
  "author": "AlexGyver <alex@alexgyver.ru>",
16
16
  "license": "MIT",
17
17
  "devDependencies": {
18
- "webpack": "^5.101.3",
18
+ "webpack": "^5.105.0",
19
19
  "webpack-cli": "^6.0.1"
20
20
  }
21
21
  }
package/test/script.js CHANGED
@@ -1,4 +1,5 @@
1
- import { EL, Sheet, StyledComponent } from "https://gyverlibs.github.io/EL.js/EL.min.js";
1
+ // import { EL, Sheet, StyledComponent } from "https://gyverlibs.github.io/Component.js/Component.min.js";
2
+ import { EL, Sheet, StyledComponent } from "../Component.js";
2
3
 
3
4
  // кнопка наследует, стили добавляются отдельно
4
5
  class Button extends EL {
@@ -105,6 +106,29 @@ class ShadowComponent {
105
106
 
106
107
  document.addEventListener('kek', () => console.log('kek!'));
107
108
 
109
+ let context = { ctx: 'ctx' };
110
+
111
+ let d = EL.make('div', {
112
+ context: context,
113
+ parent: document.body,
114
+ text: 'test',
115
+ style: { width: '100px', height: '50px', background: 'red' },
116
+ animate: { width: '200px', background: 'blue', duration: 500, onEnd: function (el) { console.log('anim', this, el) } },
117
+ events: {
118
+ mouseleave: function (e, el) { console.log('leave', this, e, el) },
119
+ },
120
+ click: (e, el, ctx) => console.log('click', this, e, el, ctx),
121
+ also: function (el) { console.log('also', this, el) },
122
+ onUpdate: (el, ctx) => console.log('update', this, el, ctx),
123
+ onDestroy: (el, ctx) => console.log('destroy', this, el, ctx),
124
+ // onMount: el => console.log('mount', this),
125
+ onMount: function (el) { console.log('mount', this, el) },
126
+ });
127
+
128
+ // setTimeout(() => { document.body.appendChild(d) }, 2000);
129
+ setTimeout(() => { EL.config(d, { text: 123 }) }, 2000);
130
+ setTimeout(() => { EL.remove(d) }, 4000);
131
+
108
132
  document.addEventListener("DOMContentLoaded", () => {
109
133
  EL.make('h1', {
110
134
  text: 'Hello!',