@alexgyver/component 1.5.0 → 2.0.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/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@alexgyver/component",
3
- "version": "1.5.0",
4
- "description": "Simple HTML&SVG element builder",
5
- "main": "./Component.js",
6
- "module": "./Component.js",
7
- "types": "./Component.js",
3
+ "version": "2.0.0",
4
+ "description": "Simple reactive HTML&SVG element builder",
5
+ "main": "./src/Component.js",
6
+ "module": "./src/Component.js",
7
+ "types": "./src/Component.js",
8
8
  "scripts": {
9
- "build": "webpack --config ./webpack.config.js"
9
+ "build": "webpack --config ./webpack.config.js",
10
+ "dev": "webpack serve --config ./webpack.dev.config.js & webpack --config ./webpack.dev.config.js"
10
11
  },
11
12
  "repository": {
12
13
  "type": "git",
@@ -15,7 +16,13 @@
15
16
  "author": "AlexGyver <alex@alexgyver.ru>",
16
17
  "license": "MIT",
17
18
  "devDependencies": {
18
- "webpack": "^5.105.0",
19
- "webpack-cli": "^6.0.1"
19
+ "webpack": "^5.105.2",
20
+ "webpack-cli": "^6.0.1",
21
+ "webpack-dev-server": "^5.2.3",
22
+ "compression-webpack-plugin": "^11.1.0",
23
+ "ifdef-loader": "^2.3.2",
24
+ "css-loader": "^7.1.3",
25
+ "style-loader": "^4.0.0",
26
+ "mini-css-extract-plugin": "^2.10.0"
20
27
  }
21
- }
28
+ }
@@ -0,0 +1,15 @@
1
+ import { EL } from "./EL";
2
+ export { EL };
3
+
4
+ /// #if !PICO_COMPONENT
5
+ import { SVG } from "./SVG";
6
+ export { SVG };
7
+
8
+ import { addCSS, removeCSS, watchMount, watchResize } from "./utils";
9
+ export { addCSS, removeCSS, watchMount, watchResize };
10
+ /// #endif
11
+
12
+ /// #if !TINY_COMPONENT
13
+ import { State, useState } from "./State";
14
+ export { State, useState };
15
+ /// #endif
package/src/EL.js ADDED
@@ -0,0 +1,371 @@
1
+ /// #if !TINY_COMPONENT
2
+ import { watchMount, watchResize } from "./utils";
3
+ /// #endif
4
+
5
+ export class EL {
6
+ // Создать элемент и поместить его в переменную $root
7
+ constructor(tag, cfg = {}, svg = false) {
8
+ this.$root = EL.make(tag, { ...cfg, ctx: this }, svg);
9
+ }
10
+
11
+ //#region ## make
12
+
13
+ // Создать элемент
14
+ static make(tag, cfg = {}, svg = false) {
15
+ tag ??= cfg.tag;
16
+
17
+ let el = (tag == 'svg' || svg)
18
+ ? document.createElementNS("http://www.w3.org/2000/svg", tag ?? 'svg')
19
+ : document.createElement(tag ?? 'div');
20
+
21
+ EL.update(el, cfg);
22
+
23
+ /// #if !TINY_COMPONENT
24
+ for (let k of EL_METHODS) {
25
+ el[k] = (...a) => EL[k](el, ...a);
26
+ }
27
+
28
+ CALLBACKS.forEach(fn => {
29
+ if (fn in cfg) el['_' + fn] = cfg[fn].bind(el.ctx, { el, ctx: el.ctx });
30
+ });
31
+
32
+ if (el._onResize) watchResize(el, el._onResize);
33
+ /// #endif
34
+
35
+ return el;
36
+ }
37
+
38
+ //#region ## update
39
+
40
+ // Обновить элемент
41
+ static update(el, cfg) {
42
+ if (!is.node(el) || !is.obj(cfg)) return el;
43
+
44
+ el.ctx = cfg.ctx ?? cfg.context ?? el.ctx;
45
+
46
+ for (let [param, val] of Object.entries(cfg)) _update(el, param, val);
47
+
48
+ /// #if !TINY_COMPONENT
49
+ if (el._onUpdate) el._onUpdate();
50
+ /// #endif
51
+
52
+ if (cfg.also) cfg.also.call(el.ctx, { el, ctx: el.ctx });
53
+
54
+ return el;
55
+ }
56
+
57
+ //#region ## lifecycle
58
+
59
+ // Подключить к родителю, null - отключить, вернёт Promise
60
+ static mount(el, parent, waitRender = false, tries = 100) {
61
+ if (el) {
62
+ /// #if !TINY_COMPONENT
63
+ if (parent == null) {
64
+ if (el.parentNode) el.parentNode.removeChild(el);
65
+ } else {
66
+ /// #endif
67
+ if (el.parentNode !== parent) parent.appendChild(el);
68
+ /// #if !TINY_COMPONENT
69
+ if (tries) return watchMount(el, waitRender, tries);
70
+ }
71
+ /// #endif
72
+ }
73
+ return Promise.resolve(el);
74
+ }
75
+
76
+ // Удалить всех детей
77
+ static clear(el, recursive = true) {
78
+ if (!el) return;
79
+ while (el.firstChild) {
80
+ /// #if !TINY_COMPONENT
81
+ if (recursive) EL.clear(el.firstChild, true);
82
+ /// #endif
83
+ EL.remove(el.firstChild, false);
84
+ }
85
+ }
86
+
87
+ // Удалить элемент
88
+ static remove(el, recursive = true) {
89
+ if (!el) return;
90
+ /// #if !TINY_COMPONENT
91
+ if (recursive) EL.clear(el, true);
92
+ EL.release(el);
93
+ EL.unbind(el);
94
+ if (el._onDestroy) el._onDestroy();
95
+ CALLBACKS.forEach(fn => el['_' + fn] = null);
96
+ /// #endif
97
+ if (el.parentNode) el.parentNode.removeChild(el); // remove
98
+ }
99
+
100
+ /// #if !TINY_COMPONENT
101
+
102
+ // Заменить ребёнка old на нового el, old удалить, у el запустить монтаж с вызовом обработчиков. Вернёт el
103
+ static replace(old, el, keepContext = false) {
104
+ if (old) {
105
+ if (old.parentNode) old.parentNode.replaceChild(el, old);
106
+ if (keepContext) el.ctx = old.ctx;
107
+ watchMount(el);
108
+ old.remove();
109
+ }
110
+ return el;
111
+ }
112
+
113
+ // Отключить on-обработчики
114
+ static release(el) {
115
+ if (el && el._events) {
116
+ el._events.forEach(({ ev, fn, options }) => el.removeEventListener(ev, fn, options));
117
+ el._events = [];
118
+ }
119
+ }
120
+
121
+ // Отключить state-бинды
122
+ static unbind(el) {
123
+ if (el && el._unsub) {
124
+ Object.values(el._unsub).forEach(f => f());
125
+ el._unsub = {};
126
+ // el._unsub.forEach(f => f());
127
+ // el._unsub = [];
128
+ }
129
+ }
130
+
131
+ /// #endif
132
+
133
+ //#region ## shadow
134
+
135
+ /// #if !TINY_COMPONENT
136
+
137
+ // Создать теневой элемент от указанного тега/Node host, дети подключатся к нему в shadowRoot, стили запишутся в $style
138
+ static makeShadow(host, cfg = {}, css = '') {
139
+ if (!host || !is.obj(cfg)) return null;
140
+
141
+ if (!is.node(host)) host = document.createElement(host);
142
+ host.attachShadow({ mode: 'open' });
143
+
144
+ EL.update(host.shadowRoot, {
145
+ ctx: cfg.context ?? cfg.ctx,
146
+ child: [{ tag: 'style', $: 'style', text: css }, cfg.child, cfg.children],
147
+ });
148
+ delete cfg.child;
149
+ delete cfg.children;
150
+ return EL.update(host, cfg);
151
+ }
152
+
153
+ /// #endif
154
+
155
+ //#region ## template
156
+
157
+ /// #if !TINY_COMPONENT
158
+
159
+ // Определить глобальный шаблон, fn - функция, возвращающая cfg-конфиг
160
+ static setTemplate(name, tag, fn) {
161
+ EL.templates.set(name, (...args) => EL.make(tag, fn(...args)));
162
+ }
163
+
164
+ // Вызвать шаблон
165
+ static useTemplate(name, ...args) {
166
+ const t = EL.templates.get(name);
167
+ return t ? t(...args) : null;
168
+ }
169
+
170
+ static templates = new Map();
171
+
172
+ /// #endif
173
+
174
+ // legacy
175
+ static config = EL.update;
176
+ }
177
+
178
+ //#region ## private
179
+
180
+ /// #if !TINY_COMPONENT
181
+ const EL_METHODS = ['update', 'mount', 'clear', 'remove'];
182
+ const CALLBACKS = ['onMount', 'onRender', 'onUpdate', 'onResize', 'onDestroy'];
183
+ /// #endif
184
+
185
+ const SKIP_PARAM = new Set(['tag', 'get', 'also', 'context', 'ctx',
186
+ /// #if !TINY_COMPONENT
187
+ ...CALLBACKS
188
+ /// #endif
189
+ ]);
190
+
191
+ const PARAM_ALIAS = {
192
+ children: 'child',
193
+ children_r: 'child_r',
194
+ text: 'textContent',
195
+ html: 'innerHTML',
196
+ };
197
+
198
+ const PARAM_UPD = {
199
+ push(el, val) {
200
+ val.push(el);
201
+ },
202
+ $(el, val) {
203
+ if (el.ctx) el.ctx['$' + val] = el;
204
+ },
205
+ attrs(el, val) {
206
+ _applyP('attrs', el, val, (k, v) => el.setAttribute(k, v), k => el.removeAttribute(k));
207
+ },
208
+ data(el, val) {
209
+ _applyP('data', el, val, (k, v) => el.dataset[k] = v, k => delete el.dataset[k]);
210
+ },
211
+ props(el, val) {
212
+ for (let p in val) {
213
+ let v = _makeVal(el, p, null, val[p]);
214
+ el[p] = (
215
+ ['min', 'max', 'step', 'selectedIndex'].includes(p)
216
+ || (p == 'value' && ['number', 'range'].includes(el.type)))
217
+ ? ((v == null || v === '' || Number.isNaN(v)) ? '' : +v)
218
+ : (v ?? '');
219
+ }
220
+ },
221
+ transition(el, val) {
222
+ const { duration = 300, easing = 'ease', delay = 0, onEnd = null, ...styles } = val;
223
+ el.style.transition = Object.keys(styles).map(st => `${st} ${duration}ms ${easing} ${delay}ms`).join(', ');
224
+ if (onEnd) {
225
+ PARAM_UPD.events(el, {
226
+ transitionend: { handler: onEnd, once: true }
227
+ });
228
+ }
229
+ requestAnimationFrame(() => PARAM_UPD.style(el, styles));
230
+ },
231
+ events(el, val) {
232
+ for (let ev in val) {
233
+ let h = val[ev];
234
+ let options = {};
235
+
236
+ if (is.obj(h)) {
237
+ options = h;
238
+ h = h.handler;
239
+ if (!h) continue;
240
+ }
241
+
242
+ const fn = (evt) => h.call(el.ctx, Object.assign(evt, { el, ctx: el.ctx }));
243
+ el.addEventListener(ev, fn, options);
244
+ /// #if !TINY_COMPONENT
245
+ (el._events ??= []).push({ ev, fn, options });
246
+ /// #endif
247
+ }
248
+ },
249
+ events_r(el, val) {
250
+ /// #if !TINY_COMPONENT
251
+ EL.release(el);
252
+ /// #endif
253
+ PARAM_UPD.events(el, val);
254
+ },
255
+ child(el, val) {
256
+ _addChild(el, val);
257
+ },
258
+ child_r(el, val) {
259
+ EL.clear(el);
260
+ _addChild(el, val);
261
+ },
262
+ style(el, val) {
263
+ _applyObj('style', el, val,
264
+ r => el.style.cssText += r + ';',
265
+ (k, v) => {
266
+ (k.startsWith('--')) ? el.style.setProperty(k, v) : el.style[k] = v;
267
+ }
268
+ );
269
+ },
270
+ style_r(el, val) {
271
+ el.style.cssText = '';
272
+ PARAM_UPD.style(el, val);
273
+ },
274
+ class(el, val) {
275
+ if (is.arr(val)) val = Object.fromEntries(val.filter(Boolean).map(c => [c, true]));
276
+ _applyObj('class', el, val,
277
+ r => r.split(/\s+/).forEach(c => c && el.classList.add(c)),
278
+ (k, v) => v ? el.classList.add(k) : el.classList.remove(k)
279
+ );
280
+ },
281
+ class_r(el, val) {
282
+ el.className = '';
283
+ PARAM_UPD.class(el, val);
284
+ },
285
+ parent(el, val) {
286
+ EL.mount(el, val, false, (el._onMount || el._onRender) ? 100 : 0);
287
+ },
288
+ };
289
+
290
+ function _applyP(param, el, val, set, del) {
291
+ for (let p in val) {
292
+ let v = _makeVal(el, param, p, val[p]);
293
+ (v == null) ? del(p) : set(p, String(v));
294
+ }
295
+ }
296
+
297
+ function _applyObj(param, el, val, raw, add) {
298
+ if (is.str(val)) {
299
+ raw(val);
300
+ } else {
301
+ for (let k in val) {
302
+ if (k == '_raw') PARAM_UPD[param](el, val[k]);
303
+ else add(k, _makeVal(el, param, k, val[k]));
304
+ }
305
+ }
306
+ }
307
+
308
+ function _addChild(el, obj) {
309
+ if (!obj) return;
310
+ else if (is.str(obj)) el.insertAdjacentHTML('beforeend', obj);
311
+ else if (is.arr(obj)) obj.forEach(o => _addChild(el, o));
312
+ else if (is.node(obj)) EL.mount(obj, el);
313
+ else if (obj instanceof EL) EL.mount(obj.$root, el);
314
+ else if (is.obj(obj)) EL.make(null, { ctx: el.ctx, ...obj, parent: el }, el instanceof SVGElement);
315
+ }
316
+
317
+ function _makeVal(el, param, sub, val) {
318
+ /// #if !TINY_COMPONENT
319
+ if (is.func(val)) {
320
+ return val.call(el.ctx, { el, ctx: el.ctx });
321
+ }
322
+
323
+ if (is.arr(val) && is.state(val[0])) {
324
+ return val.map(v => _makeVal(el, param, sub, v))[0];
325
+ }
326
+
327
+ if (is.state(val)) {
328
+ const fn = (name, value) => {
329
+ value = val.map({ el, ctx: el.ctx, name, value });
330
+ _update(el, param, sub ? { [sub]: value } : value);
331
+ if (el._onUpdate) el._onUpdate();
332
+ }
333
+ const key = param + '.' + sub + '.' + val.name;
334
+ if (!el._unsub) el._unsub = {};
335
+ if (key in el._unsub) el._unsub[key]();
336
+ el._unsub[key] = val._state.subscribe(val.name, fn);
337
+ // (el._unsub ??= []).push(val._state.subscribe(val.name, fn));
338
+ return val.map({ el, ctx: el.ctx, name: val.name, value: val._state[val.name] });
339
+ }
340
+ /// #endif
341
+
342
+ return val;
343
+ }
344
+
345
+ function _update(el, param, val) {
346
+ if (SKIP_PARAM.has(param)) return;
347
+
348
+ param = PARAM_ALIAS[param] ?? param;
349
+
350
+ if (param.startsWith('on')) {
351
+ val = { [param.slice(2).toLowerCase()]: val };
352
+ param = 'events';
353
+ } else {
354
+ /// #if !TINY_COMPONENT
355
+ val = _makeVal(el, param, null, val);
356
+ /// #endif
357
+ }
358
+
359
+ const fn = PARAM_UPD[param];
360
+ if (val != null && fn) fn(el, val);
361
+ else PARAM_UPD.props(el, { [param]: val });
362
+ }
363
+
364
+ const is = {
365
+ func: x => typeof x === 'function',
366
+ str: x => typeof x === 'string',
367
+ arr: x => Array.isArray(x),
368
+ node: x => x instanceof Node,
369
+ obj: x => x !== null && typeof x === 'object',
370
+ state: x => x && x._state,
371
+ }
package/src/SVG.js ADDED
@@ -0,0 +1,18 @@
1
+ import { EL } from "./EL";
2
+
3
+ export class SVG {
4
+ static svg = (attrs = {}, cfg = {}) => ({ tag: 'svg', ...cfg, attrs });
5
+ static rect = (x, y, width, height, rx, ry, attrs = {}, cfg = {}) => ({ tag: 'rect', ...cfg, attrs: { ...attrs, x, y, width, height, rx, ry } });
6
+ static circle = (cx, cy, r, attrs = {}, cfg = {}) => ({ tag: 'circle', ...cfg, attrs: { ...attrs, cx, cy, r } });
7
+ static line = (x1, y1, x2, y2, attrs = {}, cfg = {}) => ({ tag: 'line', ...cfg, attrs: { ...attrs, x1, y1, x2, y2 } });
8
+ static polyline = (points, attrs = {}, cfg = {}) => ({ tag: 'polyline', ...cfg, attrs: { ...attrs, points } });
9
+ static polygon = (points, attrs = {}, cfg = {}) => ({ tag: 'polygon', ...cfg, attrs: { ...attrs, points } });
10
+ static path = (d, attrs = {}, cfg = {}) => ({ tag: 'path', ...cfg, attrs: { ...attrs, d } });
11
+ static text = (text, x, y, attrs = {}, cfg = {}) => ({ tag: 'text', ...cfg, text, attrs: { ...attrs, x, y } });
12
+
13
+ static make = (tag, cfg) => EL.make(tag, cfg, true);
14
+ static update = EL.update;
15
+ static config = EL.update; // legacy
16
+ }
17
+
18
+ Object.getOwnPropertyNames(SVG).forEach(name => SVG['make_' + name] = (...args) => SVG.make(null, SVG[name](...args)));
package/src/Sheet.js ADDED
@@ -0,0 +1,73 @@
1
+ import { EL } from "./EL";
2
+
3
+ //#region Sheet
4
+ export class Sheet {
5
+ /**
6
+ * Добавить стиль с уникальным id в head. ext - стиль можно будет удалить по id
7
+ * @param {string|array} style стили в виде css строки
8
+ * @param {string|this} id уникальный id стиля. При передаче this будет именем класса
9
+ * @param {boolean} ext внешний стиль - может быть удалён по id
10
+ */
11
+ static addStyle(style, id, ext = false) {
12
+ if (!style || !id) return;
13
+ if (typeof id === 'object') id = id.constructor.name;
14
+
15
+ if (!Sheet.#int.has(id) && !Sheet.#ext.has(id)) {
16
+ if (ext) {
17
+ let sheet = document.createElement('style');
18
+ document.head.appendChild(sheet);
19
+ sheet.textContent = style;
20
+ Sheet.#ext.set(id, sheet);
21
+ } else {
22
+ if (!Sheet.#sheet) Sheet.#sheet = document.head.appendChild(document.createElement('style'));
23
+ Sheet.#sheet.textContent += style + '\r\n';
24
+ Sheet.#int.add(id);
25
+ }
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Удалить ext стиль по его id
31
+ * @param {string} id id стиля. При передаче this будет именем класса
32
+ */
33
+ static removeStyle(id) {
34
+ if (typeof id === 'object') id = id.constructor.name;
35
+ if (Sheet.#ext.has(id)) {
36
+ Sheet.#ext.get(id).remove();
37
+ Sheet.#ext.delete(id);
38
+ }
39
+ }
40
+
41
+ static #sheet = null;
42
+ static #int = new Set();
43
+ static #ext = new Map();
44
+ }
45
+
46
+ //#region StyledComponent
47
+ export class StyledComponent extends EL {
48
+ /**
49
+ * Создать элемент и поместить его в переменную $root
50
+ * @param {string} tag html tag элемента
51
+ * @param {object} data параметры
52
+ * @param {string|array} style стили в виде css строки
53
+ * @param {string|this} id уникальный id стиля. При передаче this будет именем класса
54
+ * @param {boolean} ext внешний стиль - может быть удалён по id
55
+ */
56
+ constructor(tag, data = {}, style = null, id = null, ext = false) {
57
+ super(tag, data);
58
+ Sheet.addStyle(style, id, ext);
59
+ }
60
+
61
+ /**
62
+ * Создать элемент
63
+ * @param {string} tag html tag элемента
64
+ * @param {object} data параметры
65
+ * @param {string|array} style стили в виде css строки
66
+ * @param {string|this} id уникальный id стиля. При передаче this будет именем класса
67
+ * @param {boolean} ext внешний стиль - может быть удалён по id
68
+ */
69
+ static make(tag, data = {}, style = null, id = null, ext = false) {
70
+ Sheet.addStyle(style, id, ext);
71
+ return EL.make(tag, data);
72
+ }
73
+ }
package/src/State.js ADDED
@@ -0,0 +1,47 @@
1
+ export class State {
2
+ constructor(init = {}) {
3
+ this._data = {};
4
+ this._subs = new Map();
5
+ this.addStates(init);
6
+ }
7
+
8
+ // добавить состояния
9
+ addStates(obj) {
10
+ for (const name in obj) {
11
+ if (name in this || name in this._data) continue;
12
+
13
+ this._data[name] = obj[name];
14
+
15
+ Object.defineProperty(this, name, {
16
+ get: () => this._data[name],
17
+ set: (value) => {
18
+ if (Object.is(this._data[name], value)) return;
19
+ this._data[name] = value;
20
+ const subs = this._subs.get(name);
21
+ if (subs) subs.forEach(fn => fn(name, value));
22
+ }
23
+ });
24
+ }
25
+ }
26
+
27
+ // имеет состояние
28
+ hasState(name) {
29
+ return name in this._data;
30
+ }
31
+
32
+ // подключить состояние
33
+ bind(name, map = (e) => e.value) {
34
+ return { _state: this, name, map };
35
+ }
36
+
37
+ // подписаться, функция вида fn(name, val)
38
+ subscribe(name, fn) {
39
+ if (!this._subs.has(name)) this._subs.set(name, new Set());
40
+ this._subs.get(name).add(fn);
41
+ return () => this._subs.get(name).delete(fn);
42
+ }
43
+ }
44
+
45
+ export function useState(init) {
46
+ return new State(init);
47
+ }
package/src/utils.js ADDED
@@ -0,0 +1,94 @@
1
+ //#region CSS
2
+
3
+ // Добавить стили, уникально. Без ID будет вычислен хэш
4
+ export function addCSS(css, id = '') {
5
+ if (!id) id = _hash(css);
6
+ if (cssMap.has(id)) return;
7
+
8
+ const style = document.createElement('style');
9
+ style.textContent = css;
10
+ document.head.appendChild(style);
11
+ cssMap.set(id, style);
12
+ return style;
13
+ }
14
+
15
+ // Удалить стили. Без ID будет вычислен хэш
16
+ export function removeCSS(css, id = '') {
17
+ if (!id) id = _hash(css);
18
+ const style = cssMap.get(id);
19
+ if (!style) return;
20
+
21
+ cssMap.delete(id);
22
+ style.remove();
23
+ }
24
+
25
+ function _hash(str) {
26
+ let hash = 5381;
27
+ for (let i = 0; i < str.length; i++) {
28
+ hash = ((hash << 5) + hash) + str.charCodeAt(i);
29
+ hash |= 0;
30
+ }
31
+ return hash.toString(36);
32
+ }
33
+
34
+ const cssMap = new Map(); // hash: styleElement
35
+
36
+ //#region watch
37
+
38
+ // проверить статус подключения в DOM и рендера
39
+ export function watchMount(el, waitRender = false, tries = 100) {
40
+ return new Promise(res => {
41
+ let mounted, rendered;
42
+ const check = () => {
43
+ if (el.isConnected) {
44
+ if (!el._mounted) {
45
+ el._mounted = true;
46
+ if (el._onMount) el._onMount();
47
+ }
48
+ if (!mounted) {
49
+ mounted = true;
50
+ if (!waitRender) res(el);
51
+ }
52
+
53
+ if (el.clientWidth || el.clientHeight) {
54
+ if (!el._rendered) {
55
+ el._rendered = true;
56
+ if (el._onRender) el._onRender();
57
+ }
58
+ if (!rendered) {
59
+ rendered = true;
60
+ if (waitRender) res(el);
61
+ }
62
+ return;
63
+ }
64
+ }
65
+ if (tries--) {
66
+ requestAnimationFrame(check);
67
+ } else {
68
+ if ((waitRender && !rendered) || (!waitRender && !mounted)) res(null);
69
+ }
70
+ };
71
+
72
+ check();
73
+ });
74
+ }
75
+
76
+ // следить за обновлениями размера элемента
77
+ export function watchResize(el, onResize) {
78
+ const ro = new ResizeObserver(entries => {
79
+ for (const entry of entries) {
80
+ if (entry.target === el) onResize(entry);
81
+ }
82
+ });
83
+
84
+ ro.observe(el);
85
+
86
+ const mo = new MutationObserver(() => {
87
+ if (!el.isConnected) {
88
+ ro.disconnect();
89
+ mo.disconnect();
90
+ }
91
+ });
92
+
93
+ mo.observe(document.body, { childList: true, subtree: true });
94
+ }
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <script src="./script.js" type="module"></script>
7
+ </head>
8
+
9
+ <body>
10
+ </body>
11
+
12
+ </html>