@mulsense/xnew 0.6.2 → 0.6.4

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/xnew.mjs CHANGED
@@ -96,7 +96,19 @@ class MapMap extends Map {
96
96
  }
97
97
 
98
98
  //----------------------------------------------------------------------------------------------------
99
- // ticker
99
+ // visibility change
100
+ //----------------------------------------------------------------------------------------------------
101
+ class Visibility {
102
+ constructor(callback) {
103
+ this.listener = () => callback === null || callback === void 0 ? void 0 : callback(document.hidden === false);
104
+ document.addEventListener('visibilitychange', this.listener);
105
+ }
106
+ clear() {
107
+ document.removeEventListener('visibilitychange', this.listener);
108
+ }
109
+ }
110
+ //----------------------------------------------------------------------------------------------------
111
+ // animation ticker
100
112
  //----------------------------------------------------------------------------------------------------
101
113
  class AnimationTicker {
102
114
  constructor(callback, fps = 60) {
@@ -122,91 +134,88 @@ class AnimationTicker {
122
134
  }
123
135
  class Timer {
124
136
  constructor(options) {
137
+ var _a, _b;
125
138
  this.options = options;
126
- this.startid = null;
127
- this.endid = null;
128
- this.time = 0.0;
129
- this.counter = 0;
130
- this.offset = 0.0;
131
- this.status = 0;
132
- this.ticker = new AnimationTicker((time) => {
133
- var _a, _b;
134
- let p = Math.min(this.elapsed() / this.options.duration, 1.0);
135
- if (this.options.easing === 'ease-out') {
136
- p = Math.pow((1.0 - Math.pow((1.0 - p), 2.0)), 0.5);
137
- }
138
- else if (this.options.easing === 'ease-in') {
139
- p = Math.pow((1.0 - Math.pow((1.0 - p), 0.5)), 2.0);
140
- }
141
- else if (this.options.easing === 'ease' || this.options.easing === 'ease-in-out') {
142
- // p = (1.0 - Math.cos(p * Math.PI)) / 2.0;
143
- const bias = (this.options.easing === 'ease') ? 0.7 : 1.0;
144
- const s = Math.pow(p, bias);
145
- p = s * s * (3 - 2 * s);
146
- }
147
- (_b = (_a = this.options).transition) === null || _b === void 0 ? void 0 : _b.call(_a, p);
148
- });
149
- this.visibilitychange = () => document.hidden === false ? this._start() : this._stop();
150
- document.addEventListener('visibilitychange', this.visibilitychange);
151
- this.startid = setTimeout(() => {
152
- var _a, _b;
153
- (_b = (_a = this.options).transition) === null || _b === void 0 ? void 0 : _b.call(_a, 0.0);
154
- }, 0);
139
+ this.id = null;
140
+ this.time = { start: 0.0, processed: 0.0 };
141
+ this.request = true;
142
+ this.ticker = new AnimationTicker(() => this.animation());
143
+ this.visibility = new Visibility((visible) => visible ? this._start() : this._stop());
144
+ (_b = (_a = this.options).transition) === null || _b === void 0 ? void 0 : _b.call(_a, 0.0);
155
145
  this.start();
156
146
  }
157
- clear() {
158
- if (this.startid !== null) {
159
- clearTimeout(this.startid);
160
- this.startid = null;
147
+ animation() {
148
+ var _a, _b;
149
+ let p = Math.min(this.elapsed() / this.options.duration, 1.0);
150
+ if (this.options.easing === 'ease-out') {
151
+ p = Math.pow((1.0 - Math.pow((1.0 - p), 2.0)), 0.5);
152
+ }
153
+ else if (this.options.easing === 'ease-in') {
154
+ p = Math.pow((1.0 - Math.pow((1.0 - p), 0.5)), 2.0);
155
+ }
156
+ else if (this.options.easing === 'ease' || this.options.easing === 'ease-in-out') {
157
+ const bias = (this.options.easing === 'ease') ? 0.7 : 1.0;
158
+ const s = p ** bias;
159
+ p = s * s * (3 - 2 * s);
161
160
  }
162
- if (this.endid !== null) {
163
- clearTimeout(this.endid);
164
- this.endid = null;
161
+ (_b = (_a = this.options).transition) === null || _b === void 0 ? void 0 : _b.call(_a, p);
162
+ }
163
+ clear() {
164
+ if (this.id !== null) {
165
+ clearTimeout(this.id);
166
+ this.id = null;
165
167
  }
166
- document.removeEventListener('visibilitychange', this.visibilitychange);
168
+ this.visibility.clear();
167
169
  this.ticker.clear();
168
170
  }
169
171
  elapsed() {
170
- return this.offset + (this.endid !== null ? (Date.now() - this.time) : 0);
172
+ return this.time.processed + (this.id !== null ? (Date.now() - this.time.start) : 0);
171
173
  }
172
174
  start() {
173
- this.status = 1;
175
+ this.request = true;
174
176
  this._start();
175
177
  }
176
178
  stop() {
177
179
  this._stop();
178
- this.status = 0;
180
+ this.request = false;
179
181
  }
180
182
  _start() {
181
- if (this.status === 1 && this.endid === null) {
182
- this.endid = setTimeout(() => {
183
+ if (this.request === true && this.id === null) {
184
+ this.id = setTimeout(() => {
183
185
  var _a, _b, _c, _d;
186
+ this.id = null;
187
+ this.time = { start: 0.0, processed: 0.0 };
184
188
  (_b = (_a = this.options).transition) === null || _b === void 0 ? void 0 : _b.call(_a, 1.0);
185
- (_d = (_c = this.options).timeout) === null || _d === void 0 ? void 0 : _d.call(_c);
186
- this.endid = null;
187
- this.time = 0.0;
188
- this.offset = 0.0;
189
- this.counter++;
190
- if (this.options.iterations === 0 || this.counter < this.options.iterations) {
191
- this.start();
192
- }
193
- else {
194
- this.clear();
195
- }
196
- }, this.options.duration - this.offset);
197
- this.time = Date.now();
189
+ (_d = (_c = this.options).callback) === null || _d === void 0 ? void 0 : _d.call(_c);
190
+ this.clear();
191
+ }, this.options.duration - this.time.processed);
192
+ this.time.start = Date.now();
198
193
  }
199
194
  }
200
195
  _stop() {
201
- if (this.status === 1 && this.endid !== null) {
202
- this.offset = this.offset + Date.now() - this.time;
203
- clearTimeout(this.endid);
204
- this.endid = null;
205
- this.time = 0.0;
196
+ if (this.request === true && this.id !== null) {
197
+ this.time.processed = this.time.processed + Date.now() - this.time.start;
198
+ clearTimeout(this.id);
199
+ this.id = null;
206
200
  }
207
201
  }
208
202
  }
209
203
 
204
+ function addEventListener(target, type, execute, options) {
205
+ let initalized = false;
206
+ const id = setTimeout(() => {
207
+ initalized = true;
208
+ target.addEventListener(type, execute, options);
209
+ }, 0);
210
+ return () => {
211
+ if (initalized === false) {
212
+ clearTimeout(id);
213
+ }
214
+ else {
215
+ target.removeEventListener(type, execute);
216
+ }
217
+ };
218
+ }
210
219
  class Eventor {
211
220
  constructor() {
212
221
  this.map = new MapMap();
@@ -214,53 +223,62 @@ class Eventor {
214
223
  add(element, type, listener, options) {
215
224
  const props = { element, type, listener, options };
216
225
  let finalize;
217
- if (props.type === 'resize') {
218
- finalize = this.resize(props);
219
- }
220
- else if (props.type === 'change') {
221
- finalize = this.change(props);
222
- }
223
- else if (props.type === 'input') {
224
- finalize = this.input(props);
225
- }
226
- else if (props.type === 'wheel') {
227
- finalize = this.wheel(props);
228
- }
229
- else if (props.type === 'click') {
230
- finalize = this.click(props);
231
- }
232
- else if (props.type === 'click.outside') {
233
- finalize = this.click_outside(props);
234
- }
235
- else if (['pointerdown', 'pointermove', 'pointerup', 'pointerover', 'pointerout'].includes(props.type)) {
236
- finalize = this.pointer(props);
237
- }
238
- else if (['pointerdown.outside', 'pointermove.outside', 'pointerup.outside'].includes(props.type)) {
239
- finalize = this.pointer_outside(props);
240
- }
241
- else if (['mousedown', 'mousemove', 'mouseup', 'mouseover', 'mouseout'].includes(props.type)) {
242
- finalize = this.mouse(props);
243
- }
244
- else if (['touchstart', 'touchmove', 'touchend', 'touchcancel'].includes(props.type)) {
245
- finalize = this.touch(props);
246
- }
247
- else if (['dragstart', 'dragmove', 'dragend'].includes(props.type)) {
248
- finalize = this.drag(props);
249
- }
250
- else if (['gesturestart', 'gesturemove', 'gestureend'].includes(props.type)) {
251
- finalize = this.gesture(props);
252
- }
253
- else if (['keydown', 'keyup'].includes(props.type)) {
254
- finalize = this.key(props);
255
- }
256
- else if (['keydown.arrow', 'keyup.arrow'].includes(props.type)) {
257
- finalize = this.key_arrow(props);
226
+ if (props.type.indexOf('window.') === 0) {
227
+ if (['window.keydown', 'window.keyup'].includes(props.type)) {
228
+ finalize = this.window_key(props);
229
+ }
230
+ else if (['window.keydown.arrow', 'window.keyup.arrow'].includes(props.type)) {
231
+ finalize = this.window_key_arrow(props);
232
+ }
233
+ else if (['window.keydown.wasd', 'window.keyup.wasd'].includes(props.type)) {
234
+ finalize = this.window_key_wasd(props);
235
+ }
236
+ else {
237
+ finalize = this.window_basic(props);
238
+ }
258
239
  }
259
- else if (['keydown.wasd', 'keyup.wasd'].includes(props.type)) {
260
- finalize = this.key_wasd(props);
240
+ else if (props.type.indexOf('document.') === 0) {
241
+ {
242
+ finalize = this.document_basic(props);
243
+ }
261
244
  }
262
245
  else {
263
- finalize = this.basic(props);
246
+ if (props.type === 'resize') {
247
+ finalize = this.element_resize(props);
248
+ }
249
+ else if (props.type === 'change') {
250
+ finalize = this.element_change(props);
251
+ }
252
+ else if (props.type === 'input') {
253
+ finalize = this.element_input(props);
254
+ }
255
+ else if (props.type === 'wheel') {
256
+ finalize = this.element_wheel(props);
257
+ }
258
+ else if (props.type === 'click') {
259
+ finalize = this.element_click(props);
260
+ }
261
+ else if (props.type === 'click.outside') {
262
+ finalize = this.element_click_outside(props);
263
+ }
264
+ else if (['pointerdown', 'pointermove', 'pointerup', 'pointerover', 'pointerout'].includes(props.type)) {
265
+ finalize = this.element_pointer(props);
266
+ }
267
+ else if (['pointerdown.outside', 'pointermove.outside', 'pointerup.outside'].includes(props.type)) {
268
+ finalize = this.element_pointer_outside(props);
269
+ }
270
+ else if (['mousedown', 'mousemove', 'mouseup', 'mouseover', 'mouseout'].includes(props.type)) {
271
+ finalize = this.element_mouse(props);
272
+ }
273
+ else if (['touchstart', 'touchmove', 'touchend', 'touchcancel'].includes(props.type)) {
274
+ finalize = this.element_touch(props);
275
+ }
276
+ else if (['dragstart', 'dragmove', 'dragend'].includes(props.type)) {
277
+ finalize = this.element_drag(props);
278
+ }
279
+ else {
280
+ finalize = this.element_basic(props);
281
+ }
264
282
  }
265
283
  this.map.set(props.type, props.listener, finalize);
266
284
  }
@@ -271,16 +289,12 @@ class Eventor {
271
289
  this.map.delete(type, listener);
272
290
  }
273
291
  }
274
- basic(props) {
275
- const execute = (event) => {
292
+ element_basic(props) {
293
+ return addEventListener(props.element, props.type, (event) => {
276
294
  props.listener({ event });
277
- };
278
- props.element.addEventListener(props.type, execute, props.options);
279
- return () => {
280
- props.element.removeEventListener(props.type, execute);
281
- };
295
+ }, props.options);
282
296
  }
283
- resize(props) {
297
+ element_resize(props) {
284
298
  const observer = new ResizeObserver((entries) => {
285
299
  for (const entry of entries) {
286
300
  props.listener({});
@@ -292,8 +306,8 @@ class Eventor {
292
306
  observer.unobserve(props.element);
293
307
  };
294
308
  }
295
- change(props) {
296
- const execute = (event) => {
309
+ element_change(props) {
310
+ return addEventListener(props.element, props.type, (event) => {
297
311
  let value = null;
298
312
  if (event.target.type === 'checkbox') {
299
313
  value = event.target.checked;
@@ -305,14 +319,10 @@ class Eventor {
305
319
  value = event.target.value;
306
320
  }
307
321
  props.listener({ event, value });
308
- };
309
- props.element.addEventListener(props.type, execute, props.options);
310
- return () => {
311
- props.element.removeEventListener(props.type, execute);
312
- };
322
+ }, props.options);
313
323
  }
314
- input(props) {
315
- const execute = (event) => {
324
+ element_input(props) {
325
+ return addEventListener(props.element, props.type, (event) => {
316
326
  let value = null;
317
327
  if (event.target.type === 'checkbox') {
318
328
  value = event.target.checked;
@@ -324,88 +334,56 @@ class Eventor {
324
334
  value = event.target.value;
325
335
  }
326
336
  props.listener({ event, value });
327
- };
328
- props.element.addEventListener(props.type, execute, props.options);
329
- return () => {
330
- props.element.removeEventListener(props.type, execute);
331
- };
337
+ }, props.options);
332
338
  }
333
- click(props) {
334
- const execute = (event) => {
339
+ element_click(props) {
340
+ return addEventListener(props.element, props.type, (event) => {
335
341
  props.listener({ event, position: pointer(props.element, event).position });
336
- };
337
- props.element.addEventListener(props.type, execute, props.options);
338
- return () => {
339
- props.element.removeEventListener(props.type, execute);
340
- };
342
+ }, props.options);
341
343
  }
342
- click_outside(props) {
343
- const execute = (event) => {
344
+ element_click_outside(props) {
345
+ return addEventListener(document, props.type.split('.')[0], (event) => {
344
346
  if (props.element.contains(event.target) === false) {
345
347
  props.listener({ event, position: pointer(props.element, event).position });
346
348
  }
347
- };
348
- document.addEventListener(props.type.split('.')[0], execute, props.options);
349
- return () => {
350
- document.removeEventListener(props.type.split('.')[0], execute);
351
- };
349
+ }, props.options);
352
350
  }
353
- pointer(props) {
354
- const execute = (event) => {
351
+ element_pointer(props) {
352
+ return addEventListener(props.element, props.type, (event) => {
355
353
  props.listener({ event, position: pointer(props.element, event).position });
356
- };
357
- props.element.addEventListener(props.type, execute, props.options);
358
- return () => {
359
- props.element.removeEventListener(props.type, execute);
360
- };
354
+ }, props.options);
361
355
  }
362
- mouse(props) {
363
- const execute = (event) => {
356
+ element_mouse(props) {
357
+ return addEventListener(props.element, props.type, (event) => {
364
358
  props.listener({ event, position: pointer(props.element, event).position });
365
- };
366
- props.element.addEventListener(props.type, execute, props.options);
367
- return () => {
368
- props.element.removeEventListener(props.type, execute);
369
- };
359
+ }, props.options);
370
360
  }
371
- touch(props) {
372
- const execute = (event) => {
361
+ element_touch(props) {
362
+ return addEventListener(props.element, props.type, (event) => {
373
363
  props.listener({ event, position: pointer(props.element, event).position });
374
- };
375
- props.element.addEventListener(props.type, execute, props.options);
376
- return () => {
377
- props.element.removeEventListener(props.type, execute);
378
- };
364
+ }, props.options);
379
365
  }
380
- pointer_outside(props) {
381
- const execute = (event) => {
366
+ element_pointer_outside(props) {
367
+ return addEventListener(document, props.type.split('.')[0], (event) => {
382
368
  if (props.element.contains(event.target) === false) {
383
369
  props.listener({ event, position: pointer(props.element, event).position });
384
370
  }
385
- };
386
- document.addEventListener(props.type.split('.')[0], execute, props.options);
387
- return () => {
388
- document.removeEventListener(props.type.split('.')[0], execute);
389
- };
371
+ }, props.options);
390
372
  }
391
- wheel(props) {
392
- const execute = (event) => {
373
+ element_wheel(props) {
374
+ return addEventListener(props.element, props.type, (event) => {
393
375
  props.listener({ event, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } });
394
- };
395
- props.element.addEventListener(props.type, execute, props.options);
396
- return () => {
397
- props.element.removeEventListener(props.type, execute);
398
- };
376
+ }, props.options);
399
377
  }
400
- drag(props) {
378
+ element_drag(props) {
401
379
  let pointermove = null;
402
380
  let pointerup = null;
403
381
  let pointercancel = null;
404
- const pointerdown = (event) => {
382
+ const pointerdown = addEventListener(props.element, 'pointerdown', (event) => {
405
383
  const id = event.pointerId;
406
384
  const position = pointer(props.element, event).position;
407
385
  let previous = position;
408
- pointermove = (event) => {
386
+ pointermove = addEventListener(window, 'pointermove', (event) => {
409
387
  if (event.pointerId === id) {
410
388
  const position = pointer(props.element, event).position;
411
389
  const delta = { x: position.x - previous.x, y: position.y - previous.y };
@@ -414,8 +392,8 @@ class Eventor {
414
392
  }
415
393
  previous = position;
416
394
  }
417
- };
418
- pointerup = (event) => {
395
+ }, props.options);
396
+ pointerup = addEventListener(window, 'pointerup', (event) => {
419
397
  if (event.pointerId === id) {
420
398
  const position = pointer(props.element, event).position;
421
399
  if (props.type === 'dragend') {
@@ -423,8 +401,8 @@ class Eventor {
423
401
  }
424
402
  remove();
425
403
  }
426
- };
427
- pointercancel = (event) => {
404
+ }, props.options);
405
+ pointercancel = addEventListener(window, 'pointercancel', (event) => {
428
406
  if (event.pointerId === id) {
429
407
  const position = pointer(props.element, event).position;
430
408
  if (props.type === 'dragend') {
@@ -432,168 +410,102 @@ class Eventor {
432
410
  }
433
411
  remove();
434
412
  }
435
- };
436
- window.addEventListener('pointermove', pointermove);
437
- window.addEventListener('pointerup', pointerup);
438
- window.addEventListener('pointercancel', pointercancel);
413
+ }, props.options);
439
414
  if (props.type === 'dragstart') {
440
415
  props.listener({ event, position, delta: { x: 0, y: 0 } });
441
416
  }
442
- };
417
+ }, props.options);
443
418
  function remove() {
444
- if (pointermove)
445
- window.removeEventListener('pointermove', pointermove);
446
- if (pointerup)
447
- window.removeEventListener('pointerup', pointerup);
448
- if (pointercancel)
449
- window.removeEventListener('pointercancel', pointercancel);
419
+ pointermove === null || pointermove === void 0 ? void 0 : pointermove();
450
420
  pointermove = null;
421
+ pointerup === null || pointerup === void 0 ? void 0 : pointerup();
451
422
  pointerup = null;
423
+ pointercancel === null || pointercancel === void 0 ? void 0 : pointercancel();
452
424
  pointercancel = null;
453
425
  }
454
- props.element.addEventListener('pointerdown', pointerdown, props.options);
455
426
  return () => {
456
- props.element.removeEventListener('pointerdown', pointerdown);
427
+ pointerdown();
457
428
  remove();
458
429
  };
459
430
  }
460
- gesture(props) {
461
- let isActive = false;
462
- const map = new Map();
463
- const element = props.element;
464
- const options = props.options;
465
- const dragstart = ({ event, position }) => {
466
- map.set(event.pointerId, position);
467
- isActive = map.size === 2 ? true : false;
468
- if (isActive === true && props.type === 'gesturestart') {
469
- props.listener({ event });
470
- }
471
- };
472
- const dragmove = ({ event, position, delta }) => {
473
- if (map.size >= 2 && isActive === true) {
474
- const a = map.get(event.pointerId);
475
- const b = getOthers(event.pointerId)[0];
476
- let scale = 0.0;
477
- {
478
- const v = { x: a.x - b.x, y: a.y - b.y };
479
- const s = v.x * v.x + v.y * v.y;
480
- scale = 1 + (s > 0.0 ? (v.x * delta.x + v.y * delta.y) / s : 0);
481
- }
482
- // let rotate = 0.0;
483
- // {
484
- // const c = { x: a.x + delta.x, y: a.y + delta.y };
485
- // const v1 = { x: a.x - b.x, y: a.y - b.y };
486
- // const v2 = { x: c.x - b.x, y: c.y - b.y };
487
- // const l1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y);
488
- // const l2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y);
489
- // if (l1 > 0.0 && l2 > 0.0) {
490
- // const angle = Math.acos((v1.x * v2.x + v1.y * v2.y) / (l1 * l2));
491
- // const sign = v1.x * v2.y - v1.y * v2.x;
492
- // rotate = sign > 0.0 ? +angle : -angle;
493
- // }
494
- // }
495
- if (props.type === 'gesturemove') {
496
- props.listener({ event, scale });
497
- }
498
- }
499
- map.set(event.pointerId, position);
500
- };
501
- const dragend = ({ event }) => {
502
- map.delete(event.pointerId);
503
- if (isActive === true && props.type === 'gestureend') {
504
- props.listener({ event, scale: 1.0 });
505
- }
506
- isActive = false;
507
- };
508
- this.add(element, 'dragstart', dragstart, options);
509
- this.add(element, 'dragmove', dragmove, options);
510
- this.add(element, 'dragend', dragend, options);
511
- function getOthers(id) {
512
- const backup = map.get(id);
513
- map.delete(id);
514
- const others = [...map.values()];
515
- map.set(id, backup);
516
- return others;
517
- }
518
- return () => {
519
- this.remove('dragstart', dragstart);
520
- this.remove('dragmove', dragmove);
521
- this.remove('dragend', dragend);
522
- };
431
+ window_basic(props) {
432
+ const type = props.type.substring('window.'.length);
433
+ return addEventListener(window, type, (event) => {
434
+ props.listener({ event });
435
+ }, props.options);
523
436
  }
524
- key(props) {
525
- const execute = (event) => {
526
- if (props.type === 'keydown' && event.repeat)
437
+ window_key(props) {
438
+ const type = props.type.substring(props.type.indexOf('.') + 1);
439
+ return addEventListener(window, type, (event) => {
440
+ if (event.repeat)
527
441
  return;
528
- props.listener({ event, code: event.code });
529
- };
530
- window.addEventListener(props.type, execute, props.options);
531
- return () => {
532
- window.removeEventListener(props.type, execute);
533
- };
442
+ props.listener({ event });
443
+ }, props.options);
534
444
  }
535
- key_arrow(props) {
445
+ window_key_arrow(props) {
536
446
  const keymap = {};
537
- const keydown = (event) => {
447
+ const keydown = addEventListener(window, 'keydown', (event) => {
538
448
  if (event.repeat)
539
449
  return;
540
450
  keymap[event.code] = 1;
541
- if (props.type === 'keydown.arrow' && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
451
+ if (props.type === 'window.keydown.arrow' && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
542
452
  const vector = {
543
453
  x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
544
454
  y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
545
455
  };
546
- props.listener({ event, code: event.code, vector });
456
+ props.listener({ event, vector });
547
457
  }
548
- };
549
- const keyup = (event) => {
458
+ }, props.options);
459
+ const keyup = addEventListener(window, 'keyup', (event) => {
550
460
  keymap[event.code] = 0;
551
- if (props.type === 'keyup.arrow' && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
461
+ if (props.type === 'window.keyup.arrow' && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
552
462
  const vector = {
553
463
  x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
554
464
  y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
555
465
  };
556
- props.listener({ event, code: event.code, vector });
466
+ props.listener({ event, vector });
557
467
  }
558
- };
559
- window.addEventListener('keydown', keydown, props.options);
560
- window.addEventListener('keyup', keyup, props.options);
468
+ }, props.options);
561
469
  return () => {
562
- window.removeEventListener('keydown', keydown);
563
- window.removeEventListener('keyup', keyup);
470
+ keydown();
471
+ keyup();
564
472
  };
565
473
  }
566
- key_wasd(props) {
474
+ window_key_wasd(props) {
567
475
  const keymap = {};
568
- const keydown = (event) => {
476
+ const finalize1 = addEventListener(window, 'keydown', (event) => {
569
477
  if (event.repeat)
570
478
  return;
571
479
  keymap[event.code] = 1;
572
- if (props.type === 'keydown.wasd' && ['KeyW', 'KeyA', 'KeyS', 'KeyD'].includes(event.code)) {
480
+ if (props.type === 'window.keydown.wasd' && ['KeyW', 'KeyA', 'KeyS', 'KeyD'].includes(event.code)) {
573
481
  const vector = {
574
482
  x: (keymap['KeyA'] ? -1 : 0) + (keymap['KeyD'] ? +1 : 0),
575
483
  y: (keymap['KeyW'] ? -1 : 0) + (keymap['KeyS'] ? +1 : 0)
576
484
  };
577
- props.listener({ event, code: event.code, vector });
485
+ props.listener({ event, vector });
578
486
  }
579
- };
580
- const keyup = (event) => {
487
+ }, props.options);
488
+ const finalize2 = addEventListener(window, 'keyup', (event) => {
581
489
  keymap[event.code] = 0;
582
- if (props.type === 'keyup.wasd' && ['KeyW', 'KeyA', 'KeyS', 'KeyD'].includes(event.code)) {
490
+ if (props.type === 'window.keyup.wasd' && ['KeyW', 'KeyA', 'KeyS', 'KeyD'].includes(event.code)) {
583
491
  const vector = {
584
492
  x: (keymap['KeyA'] ? -1 : 0) + (keymap['KeyD'] ? +1 : 0),
585
493
  y: (keymap['KeyW'] ? -1 : 0) + (keymap['KeyS'] ? +1 : 0)
586
494
  };
587
- props.listener({ event, code: event.code, vector });
495
+ props.listener({ event, vector });
588
496
  }
589
- };
590
- window.addEventListener('keydown', keydown, props.options);
591
- window.addEventListener('keyup', keyup, props.options);
497
+ }, props.options);
592
498
  return () => {
593
- window.removeEventListener('keydown', keydown);
594
- window.removeEventListener('keyup', keyup);
499
+ finalize1();
500
+ finalize2();
595
501
  };
596
502
  }
503
+ document_basic(props) {
504
+ const type = props.type.substring('document.'.length);
505
+ return addEventListener(document, type, (event) => {
506
+ props.listener({ event });
507
+ }, props.options);
508
+ }
597
509
  }
598
510
  function pointer(element, event) {
599
511
  const rect = element.getBoundingClientRect();
@@ -606,58 +518,52 @@ function pointer(element, event) {
606
518
  //----------------------------------------------------------------------------------------------------
607
519
  const SYSTEM_EVENTS = ['start', 'update', 'render', 'stop', 'finalize'];
608
520
  class UnitPromise {
609
- constructor(promise, component) {
521
+ constructor(promise, Component) {
610
522
  this.promise = promise;
611
- this.component = component;
612
- }
613
- then(callback) {
614
- const snapshot = Unit.snapshot(Unit.currentUnit);
615
- this.promise = this.promise.then((...args) => Unit.scope(snapshot, callback, ...args));
616
- return this;
617
- }
618
- catch(callback) {
619
- const snapshot = Unit.snapshot(Unit.currentUnit);
620
- this.promise = this.promise.catch((...args) => Unit.scope(snapshot, callback, ...args));
621
- return this;
523
+ this.Component = Component;
622
524
  }
623
- finally(callback) {
525
+ then(callback) { return this.wrap('then', callback); }
526
+ catch(callback) { return this.wrap('catch', callback); }
527
+ finally(callback) { return this.wrap('finally', callback); }
528
+ wrap(key, callback) {
624
529
  const snapshot = Unit.snapshot(Unit.currentUnit);
625
- this.promise = this.promise.finally(() => Unit.scope(snapshot, callback));
530
+ this.promise = this.promise[key]((...args) => Unit.scope(snapshot, callback, ...args));
626
531
  return this;
627
532
  }
628
533
  }
629
534
  class UnitTimer {
630
- constructor(options) {
535
+ constructor() {
536
+ this.unit = null;
631
537
  this.stack = [];
632
- this.unit = new Unit(Unit.currentUnit, null, UnitTimer.Component, { options, snapshot: Unit.snapshot(Unit.currentUnit) });
633
538
  }
634
539
  clear() {
540
+ var _a;
635
541
  this.stack = [];
636
- this.unit.finalize();
542
+ (_a = this.unit) === null || _a === void 0 ? void 0 : _a.finalize();
543
+ this.unit = null;
637
544
  }
638
- timeout(timeout, duration = 0) {
639
- UnitTimer.execute(this, { timeout, duration, iterations: 1 });
640
- return this;
545
+ timeout(callback, duration = 0) {
546
+ return UnitTimer.execute(this, { callback, duration }, 1);
641
547
  }
642
- iteration(timeout, duration = 0, iterations = -1) {
643
- UnitTimer.execute(this, { timeout, duration, iterations });
644
- return this;
548
+ interval(callback, duration = 0, iterations = 0) {
549
+ return UnitTimer.execute(this, { callback, duration }, iterations);
645
550
  }
646
551
  transition(transition, duration = 0, easing) {
647
- UnitTimer.execute(this, { transition, duration, iterations: 1, easing });
648
- return this;
552
+ return UnitTimer.execute(this, { transition, duration, easing }, 1);
649
553
  }
650
- static execute(timer, options) {
651
- if (timer.unit._.state === 'finalized') {
652
- timer.unit = new Unit(Unit.currentUnit, null, UnitTimer.Component, { options, snapshot: Unit.snapshot(Unit.currentUnit) });
554
+ static execute(timer, options, iterations) {
555
+ const props = { options, iterations, snapshot: Unit.snapshot(Unit.currentUnit) };
556
+ if (timer.unit === null || timer.unit._.state === 'finalized') {
557
+ timer.unit = new Unit(Unit.currentUnit, null, UnitTimer.Component, props);
653
558
  }
654
559
  else if (timer.stack.length === 0) {
655
- timer.stack.push({ options, snapshot: Unit.snapshot(Unit.currentUnit) });
560
+ timer.stack.push(props);
656
561
  timer.unit.on('finalize', () => UnitTimer.next(timer));
657
562
  }
658
563
  else {
659
- timer.stack.push({ options, snapshot: Unit.snapshot(Unit.currentUnit) });
564
+ timer.stack.push(props);
660
565
  }
566
+ return timer;
661
567
  }
662
568
  static next(timer) {
663
569
  if (timer.stack.length > 0) {
@@ -665,27 +571,32 @@ class UnitTimer {
665
571
  timer.unit.on('finalize', () => UnitTimer.next(timer));
666
572
  }
667
573
  }
668
- static Component(unit, { options, snapshot }) {
574
+ static Component(unit, { options, iterations, snapshot }) {
669
575
  let counter = 0;
670
- const timer = new Timer({
671
- transition: (p) => {
672
- if (options.transition)
673
- Unit.scope(snapshot, options.transition, p);
674
- },
675
- timeout: () => {
676
- if (options.transition)
677
- Unit.scope(snapshot, options.transition, 1.0);
678
- if (options.timeout)
679
- Unit.scope(snapshot, options.timeout);
680
- if (options.iterations && counter >= options.iterations - 1) {
681
- unit.finalize();
682
- }
683
- counter++;
684
- }, duration: options.duration, iterations: options.iterations, easing: options.easing
685
- });
576
+ let timer = new Timer({ callback, transition, duration: options.duration, easing: options.easing });
577
+ function callback() {
578
+ if (options.callback)
579
+ Unit.scope(snapshot, options.callback);
580
+ if (iterations <= 0 || counter < iterations - 1) {
581
+ timer = new Timer({ callback, transition, duration: options.duration, easing: options.easing });
582
+ }
583
+ else {
584
+ unit.finalize();
585
+ }
586
+ counter++;
587
+ }
588
+ function transition(value) {
589
+ if (options.transition)
590
+ Unit.scope(snapshot, options.transition, { value });
591
+ }
686
592
  unit.on('finalize', () => timer.clear());
687
593
  }
688
594
  }
595
+ function DefaultComponent(unit, { text }) {
596
+ if (text !== undefined) {
597
+ unit.element.textContent = text;
598
+ }
599
+ }
689
600
  //----------------------------------------------------------------------------------------------------
690
601
  // unit
691
602
  //----------------------------------------------------------------------------------------------------
@@ -700,20 +611,21 @@ class Unit {
700
611
  baseElement = parent._.currentElement;
701
612
  }
702
613
  else {
703
- baseElement = document.body;
614
+ baseElement = (document === null || document === void 0 ? void 0 : document.body) ? document.body : null;
704
615
  }
705
616
  let baseComponent;
706
617
  if (typeof component === 'function') {
707
618
  baseComponent = component;
708
619
  }
709
- else if (component !== undefined) {
710
- baseComponent = (unit) => { unit.element.textContent = component.toString(); };
620
+ else if (typeof component === 'string' || typeof component === 'number') {
621
+ baseComponent = DefaultComponent;
622
+ props = { text: component.toString() };
711
623
  }
712
624
  else {
713
- baseComponent = (unit) => { };
625
+ baseComponent = DefaultComponent;
714
626
  }
715
627
  const baseContext = (_a = parent === null || parent === void 0 ? void 0 : parent._.currentContext) !== null && _a !== void 0 ? _a : { stack: null };
716
- this._ = { parent, target, baseElement, baseContext, baseComponent, props: props !== null && props !== void 0 ? props : {} };
628
+ this._ = { parent, target, baseElement, baseContext, baseComponent, props };
717
629
  parent === null || parent === void 0 ? void 0 : parent._.children.push(this);
718
630
  Unit.initialize(this, null);
719
631
  }
@@ -735,8 +647,10 @@ class Unit {
735
647
  }
736
648
  }
737
649
  reboot() {
738
- var _a, _b;
739
- const anchor = (_b = (_a = this._.elements[0]) === null || _a === void 0 ? void 0 : _a.nextElementSibling) !== null && _b !== void 0 ? _b : null;
650
+ let anchor = null;
651
+ if (this._.nestElements[0] && this._.nestElements[0].owned === true) {
652
+ anchor = this._.nestElements[0].element.nextElementSibling;
653
+ }
740
654
  Unit.stop(this);
741
655
  Unit.finalize(this);
742
656
  Unit.initialize(this, anchor);
@@ -754,7 +668,7 @@ class Unit {
754
668
  protected: false,
755
669
  ancestors: unit._.parent ? [unit._.parent, ...unit._.parent._.ancestors] : [],
756
670
  children: [],
757
- elements: [],
671
+ nestElements: [],
758
672
  promises: [],
759
673
  components: [],
760
674
  listeners: new MapMap(),
@@ -779,10 +693,12 @@ class Unit {
779
693
  [...unit._.systems.finalize].reverse().forEach(({ execute }) => execute());
780
694
  unit.off();
781
695
  unit._.components.forEach((component) => Unit.component2units.delete(component, unit));
782
- if (unit._.elements.length > 0) {
783
- unit._.baseElement.removeChild(unit._.elements[0]);
784
- unit._.currentElement = unit._.baseElement;
696
+ for (const { element, owned } of unit._.nestElements.reverse()) {
697
+ if (owned === true) {
698
+ element.remove();
699
+ }
785
700
  }
701
+ unit._.currentElement = unit._.baseElement;
786
702
  // reset defines
787
703
  Object.keys(unit._.defines).forEach((key) => {
788
704
  delete unit[key];
@@ -791,42 +707,58 @@ class Unit {
791
707
  unit._.state = 'finalized';
792
708
  }
793
709
  }
794
- static nest(unit, tag, textContent) {
795
- const match = tag.match(/<((\w+)[^>]*?)\/?>/);
796
- if (match !== null) {
797
- let element;
798
- if (unit._.anchor !== null) {
799
- unit._.anchor.insertAdjacentHTML('beforebegin', `<${match[1]}></${match[2]}>`);
800
- element = unit._.anchor.previousElementSibling;
801
- unit._.anchor = null;
710
+ static nest(unit, target, textContent) {
711
+ if (target instanceof HTMLElement || target instanceof SVGElement) {
712
+ unit._.nestElements.push({ element: target, owned: false });
713
+ unit._.currentElement = target;
714
+ return target;
715
+ }
716
+ else {
717
+ const match = target.match(/<((\w+)[^>]*?)\/?>/);
718
+ if (match !== null) {
719
+ let element;
720
+ if (unit._.anchor !== null) {
721
+ unit._.anchor.insertAdjacentHTML('beforebegin', `<${match[1]}></${match[2]}>`);
722
+ element = unit._.anchor.previousElementSibling;
723
+ unit._.anchor = null;
724
+ }
725
+ else {
726
+ unit._.currentElement.insertAdjacentHTML('beforeend', `<${match[1]}></${match[2]}>`);
727
+ element = unit._.currentElement.children[unit._.currentElement.children.length - 1];
728
+ }
729
+ unit._.currentElement = element;
730
+ if (textContent !== undefined) {
731
+ element.textContent = textContent.toString();
732
+ }
733
+ unit._.nestElements.push({ element, owned: true });
734
+ return element;
802
735
  }
803
736
  else {
804
- unit._.currentElement.insertAdjacentHTML('beforeend', `<${match[1]}></${match[2]}>`);
805
- element = unit._.currentElement.children[unit._.currentElement.children.length - 1];
737
+ throw new Error(`xnew.nest: invalid html string [${target}]`);
806
738
  }
807
- unit._.currentElement = element;
808
- if (textContent !== undefined) {
809
- element.textContent = textContent.toString();
810
- }
811
- unit._.elements.push(element);
812
- return element;
813
- }
814
- else {
815
- throw new Error(`xnew.nest: invalid html string [${tag}]`);
816
739
  }
817
740
  }
818
- static extend(unit, component, props) {
741
+ static extend(unit, Component, props) {
819
742
  var _a;
820
- if (unit._.components.includes(component) === true) {
743
+ if (unit._.components.includes(Component) === true) {
821
744
  throw new Error(`The component is already extended.`);
822
745
  }
823
746
  else {
824
747
  const backupComponent = unit._.currentComponent;
825
- unit._.currentComponent = component;
826
- const defines = (_a = component(unit, props)) !== null && _a !== void 0 ? _a : {};
748
+ unit._.currentComponent = Component;
749
+ const defines = (_a = Component(unit, props !== null && props !== void 0 ? props : {})) !== null && _a !== void 0 ? _a : {};
750
+ if (unit._.parent && Component !== DefaultComponent) {
751
+ if (Component === unit._.baseComponent) {
752
+ Unit.context(unit._.parent, Component, unit);
753
+ }
754
+ else {
755
+ Unit.context(unit, Component, unit);
756
+ Unit.context(unit._.parent, Component, unit);
757
+ }
758
+ }
827
759
  unit._.currentComponent = backupComponent;
828
- Unit.component2units.add(component, unit);
829
- unit._.components.push(component);
760
+ Unit.component2units.add(Component, unit);
761
+ unit._.components.push(Component);
830
762
  Object.keys(defines).forEach((key) => {
831
763
  if (unit[key] !== undefined && unit._.defines[key] === undefined) {
832
764
  throw new Error(`The property "${key}" already exists.`);
@@ -931,9 +863,9 @@ class Unit {
931
863
  }
932
864
  }
933
865
  }
934
- static find(component) {
866
+ static find(Component) {
935
867
  var _a;
936
- return [...((_a = Unit.component2units.get(component)) !== null && _a !== void 0 ? _a : [])];
868
+ return [...((_a = Unit.component2units.get(Component)) !== null && _a !== void 0 ? _a : [])];
937
869
  }
938
870
  on(type, listener, options) {
939
871
  const types = type.trim().split(/\s+/);
@@ -1013,54 +945,46 @@ const xnew$1 = Object.assign(function (...args) {
1013
945
  else {
1014
946
  target = null;
1015
947
  }
1016
- const component = args.shift();
948
+ const Component = args.shift();
1017
949
  const props = args.shift();
1018
- const unit = new Unit(Unit.currentUnit, target, component, props);
1019
- if (typeof component === 'function') {
1020
- Unit.context(Unit.currentUnit, component, unit);
1021
- }
1022
- return unit;
950
+ return new Unit(Unit.currentUnit, target, Component, props);
1023
951
  }, {
1024
952
  /**
1025
953
  * Creates a nested HTML/SVG element within the current component
1026
- * @param tag - HTML or SVG tag string (e.g., '<div class="my-class">', '<span style="color:red">', '<svg viewBox="0 0 24 24">')
954
+ * @param target - HTML or SVG tag string (e.g., '<div class="my-class">', '<span style="color:red">', '<svg viewBox="0 0 24 24">')
1027
955
  * @returns The created HTML/SVG element
1028
956
  * @throws Error if called after component initialization
1029
957
  * @example
1030
958
  * const div = xnew.nest('<div>')
1031
959
  * div.textContent = 'Hello'
1032
960
  */
1033
- nest(tag) {
961
+ nest(target) {
1034
962
  try {
1035
963
  if (Unit.currentUnit._.state !== 'invoked') {
1036
964
  throw new Error('xnew.nest can not be called after initialized.');
1037
965
  }
1038
- return Unit.nest(Unit.currentUnit, tag);
966
+ return Unit.nest(Unit.currentUnit, target);
1039
967
  }
1040
968
  catch (error) {
1041
- console.error('xnew.nest(tag: string): ', error);
969
+ console.error('xnew.nest(target: HTMLElement | SVGElement | string): ', error);
1042
970
  throw error;
1043
971
  }
1044
972
  },
1045
973
  /**
1046
974
  * Extends the current component with another component's functionality
1047
- * @param component - Component function to extend with
1048
- * @param props - Optional properties to pass to the extended component
975
+ * @param Component - component function to extend with
976
+ * @param props - optional properties to pass to the extended component
1049
977
  * @returns defines returned by the extended component
1050
978
  * @throws Error if called after component initialization
1051
979
  * @example
1052
980
  * const api = xnew.extend(BaseComponent, { data: {} })
1053
981
  */
1054
- extend(component, props) {
982
+ extend(Component, props) {
1055
983
  try {
1056
984
  if (Unit.currentUnit._.state !== 'invoked') {
1057
985
  throw new Error('xnew.extend can not be called after initialized.');
1058
986
  }
1059
- const defines = Unit.extend(Unit.currentUnit, component, props);
1060
- if (typeof component === 'function') {
1061
- return Unit.context(Unit.currentUnit, component, Unit.currentUnit);
1062
- }
1063
- return defines;
987
+ return Unit.extend(Unit.currentUnit, Component, props);
1064
988
  }
1065
989
  catch (error) {
1066
990
  console.error('xnew.extend(component: Function, props?: Object): ', error);
@@ -1098,8 +1022,15 @@ const xnew$1 = Object.assign(function (...args) {
1098
1022
  promise(promise) {
1099
1023
  try {
1100
1024
  const component = Unit.currentUnit._.currentComponent;
1101
- Unit.currentUnit._.promises.push(new UnitPromise(promise, component));
1102
- return Unit.currentUnit._.promises[Unit.currentUnit._.promises.length - 1];
1025
+ let unitPromise;
1026
+ if (promise instanceof Unit) {
1027
+ unitPromise = new UnitPromise(Promise.all(promise._.promises.map(p => p.promise)), component);
1028
+ }
1029
+ else {
1030
+ unitPromise = new UnitPromise(promise, component);
1031
+ }
1032
+ Unit.currentUnit._.promises.push(unitPromise);
1033
+ return unitPromise;
1103
1034
  }
1104
1035
  catch (error) {
1105
1036
  console.error('xnew.promise(promise: Promise<any>): ', error);
@@ -1115,11 +1046,11 @@ const xnew$1 = Object.assign(function (...args) {
1115
1046
  */
1116
1047
  then(callback) {
1117
1048
  try {
1118
- const component = Unit.currentUnit._.currentComponent;
1049
+ const Component = Unit.currentUnit._.currentComponent;
1119
1050
  const promises = Unit.currentUnit._.promises;
1120
1051
  return new UnitPromise(Promise.all(promises.map(p => p.promise)), null)
1121
1052
  .then((results) => {
1122
- callback(results.filter((_result, index) => promises[index].component !== null && promises[index].component === component));
1053
+ callback(results.filter((_, index) => promises[index].Component !== null && promises[index].Component === Component));
1123
1054
  });
1124
1055
  }
1125
1056
  catch (error) {
@@ -1214,27 +1145,27 @@ const xnew$1 = Object.assign(function (...args) {
1214
1145
  },
1215
1146
  /**
1216
1147
  * Executes a callback once after a delay, managed by component lifecycle
1217
- * @param timeout - Function to execute after Duration
1148
+ * @param callback - Function to execute after Duration
1218
1149
  * @param duration - Duration in milliseconds
1219
1150
  * @returns Object with clear() method to cancel the timeout
1220
1151
  * @example
1221
1152
  * const timer = xnew.timeout(() => console.log('Delayed'), 1000)
1222
1153
  * // Cancel if needed: timer.clear()
1223
1154
  */
1224
- timeout(timeout, duration = 0) {
1225
- return new UnitTimer({ timeout, duration, iterations: 1 });
1155
+ timeout(callback, duration = 0) {
1156
+ return new UnitTimer().timeout(callback, duration);
1226
1157
  },
1227
1158
  /**
1228
1159
  * Executes a callback repeatedly at specified intervals, managed by component lifecycle
1229
- * @param timeout - Function to execute at each duration
1160
+ * @param callback - Function to execute at each duration
1230
1161
  * @param duration - Duration in milliseconds
1231
1162
  * @returns Object with clear() method to stop the interval
1232
1163
  * @example
1233
1164
  * const timer = xnew.interval(() => console.log('Tick'), 1000)
1234
1165
  * // Stop when needed: timer.clear()
1235
1166
  */
1236
- interval(timeout, duration, iterations = 0) {
1237
- return new UnitTimer({ timeout, duration, iterations });
1167
+ interval(callback, duration, iterations = 0) {
1168
+ return new UnitTimer().interval(callback, duration, iterations);
1238
1169
  },
1239
1170
  /**
1240
1171
  * Creates a transition animation with easing, executing callback with progress values
@@ -1250,7 +1181,7 @@ const xnew$1 = Object.assign(function (...args) {
1250
1181
  * }, 300)
1251
1182
  */
1252
1183
  transition(transition, duration = 0, easing = 'linear') {
1253
- return new UnitTimer({ transition, duration, easing, iterations: 1 });
1184
+ return new UnitTimer().transition(transition, duration, easing);
1254
1185
  },
1255
1186
  /**
1256
1187
  * Call this method within a component function to enable protection.
@@ -1267,33 +1198,39 @@ const xnew$1 = Object.assign(function (...args) {
1267
1198
  },
1268
1199
  });
1269
1200
 
1270
- function OpenAndClose(unit, { open = false }) {
1271
- let state = open ? 1.0 : 0.0;
1201
+ function OpenAndClose(unit, { open = true, transition = { duration: 200, easing: 'ease' } }) {
1202
+ let value = open ? 1.0 : 0.0;
1272
1203
  let sign = open ? +1 : -1;
1273
- let timer = xnew$1.timeout(() => xnew$1.emit('-transition', { state }));
1204
+ let timer = xnew$1.timeout(() => xnew$1.emit('-transition', { value }));
1274
1205
  return {
1275
- toggle(duration = 200, easing = 'ease') {
1276
- sign < 0 ? unit.open(duration, easing) : unit.close(duration, easing);
1206
+ toggle() {
1207
+ sign < 0 ? unit.open() : unit.close();
1277
1208
  },
1278
- open(duration = 200, easing = 'ease') {
1209
+ open() {
1210
+ var _a, _b;
1279
1211
  sign = +1;
1280
- const d = 1 - state;
1212
+ const d = 1 - value;
1213
+ const duration = ((_a = transition === null || transition === void 0 ? void 0 : transition.duration) !== null && _a !== void 0 ? _a : 200) * d;
1214
+ const easing = (_b = transition === null || transition === void 0 ? void 0 : transition.easing) !== null && _b !== void 0 ? _b : 'ease';
1281
1215
  timer === null || timer === void 0 ? void 0 : timer.clear();
1282
- timer = xnew$1.transition((x) => {
1283
- state = 1.0 - (x < 1.0 ? (1 - x) * d : 0.0);
1284
- xnew$1.emit('-transition', { state });
1285
- }, duration * d, easing)
1286
- .timeout(() => xnew$1.emit('-opened', { state }));
1216
+ timer = xnew$1.transition(({ value: x }) => {
1217
+ value = 1.0 - (x < 1.0 ? (1 - x) * d : 0.0);
1218
+ xnew$1.emit('-transition', { value });
1219
+ }, duration, easing)
1220
+ .timeout(() => xnew$1.emit('-opened'));
1287
1221
  },
1288
- close(duration = 200, easing = 'ease') {
1222
+ close() {
1223
+ var _a, _b;
1289
1224
  sign = -1;
1290
- const d = state;
1225
+ const d = value;
1226
+ const duration = ((_a = transition === null || transition === void 0 ? void 0 : transition.duration) !== null && _a !== void 0 ? _a : 200) * d;
1227
+ const easing = (_b = transition === null || transition === void 0 ? void 0 : transition.easing) !== null && _b !== void 0 ? _b : 'ease';
1291
1228
  timer === null || timer === void 0 ? void 0 : timer.clear();
1292
- timer = xnew$1.transition((x) => {
1293
- state = x < 1.0 ? (1 - x) * d : 0.0;
1294
- xnew$1.emit('-transition', { state });
1295
- }, duration * d, easing)
1296
- .timeout(() => xnew$1.emit('-closed', { state }));
1229
+ timer = xnew$1.transition(({ value: x }) => {
1230
+ value = x < 1.0 ? (1 - x) * d : 0.0;
1231
+ xnew$1.emit('-transition', { value });
1232
+ }, duration, easing)
1233
+ .timeout(() => xnew$1.emit('-closed'));
1297
1234
  },
1298
1235
  };
1299
1236
  }
@@ -1301,23 +1238,19 @@ function Accordion(unit) {
1301
1238
  const system = xnew$1.context(OpenAndClose);
1302
1239
  const outer = xnew$1.nest('<div style="overflow: hidden;">');
1303
1240
  const inner = xnew$1.nest('<div style="display: flex; flex-direction: column; box-sizing: border-box;">');
1304
- system.on('-transition', ({ state }) => {
1305
- outer.style.height = state < 1.0 ? inner.offsetHeight * state + 'px' : 'auto';
1306
- outer.style.opacity = state.toString();
1241
+ system.on('-transition', ({ value }) => {
1242
+ outer.style.height = value < 1.0 ? inner.offsetHeight * value + 'px' : 'auto';
1243
+ outer.style.opacity = value.toString();
1307
1244
  });
1308
1245
  }
1309
- function Modal(unit) {
1246
+ function Popup(unit) {
1310
1247
  const system = xnew$1.context(OpenAndClose);
1311
- system.open();
1312
1248
  system.on('-closed', () => unit.finalize());
1313
- xnew$1.nest('<div style="position: absolute; inset: 0; z-index: 1000; opacity: 0;">');
1314
- unit.on('click', ({ event }) => {
1315
- if (event.target === unit.element) {
1316
- system.close();
1317
- }
1318
- });
1319
- system.on('-transition', ({ state }) => {
1320
- unit.element.style.opacity = state.toString();
1249
+ system.open();
1250
+ xnew$1.nest('<div style="position: fixed; inset: 0; z-index: 1000; opacity: 0;">');
1251
+ unit.on('click', ({ event }) => event.target === unit.element && system.close());
1252
+ system.on('-transition', ({ value }) => {
1253
+ unit.element.style.opacity = value.toString();
1321
1254
  });
1322
1255
  }
1323
1256
 
@@ -1339,14 +1272,14 @@ function Screen(unit, { aspect, fit = 'contain' } = {}) {
1339
1272
  function SVGTemplate(self, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = null, fillOpacity = 0.8 }) {
1340
1273
  xnew$1.nest(`<svg
1341
1274
  viewBox="0 0 64 64"
1342
- style="position: absolute; width: 100%; height: 100%; select: none;
1275
+ style="position: absolute; width: 100%; height: 100%; user-select: none;
1343
1276
  stroke: ${stroke}; stroke-opacity: ${strokeOpacity}; stroke-width: ${strokeWidth}; stroke-linejoin: ${strokeLinejoin};
1344
1277
  ${fill ? `fill: ${fill}; fill-opacity: ${fillOpacity};` : ''}
1345
1278
  ">`);
1346
1279
  }
1347
1280
  function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1348
1281
  xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1349
- xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1282
+ xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; user-select: none; pointer-events: auto; overflow: hidden;">`);
1350
1283
  xnew$1((unit) => {
1351
1284
  xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1352
1285
  xnew$1('<polygon points="32 7 27 13 37 13">');
@@ -1369,7 +1302,7 @@ function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strok
1369
1302
  target.element.style.left = `${vector.x * size / 4}px`;
1370
1303
  target.element.style.top = `${vector.y * size / 4}px`;
1371
1304
  const nexttype = { dragstart: '-down', dragmove: '-move' }[type];
1372
- xnew$1.emit(nexttype, { type: nexttype, vector });
1305
+ xnew$1.emit(nexttype, { vector });
1373
1306
  });
1374
1307
  unit.on('dragend', () => {
1375
1308
  const size = unit.element.clientWidth;
@@ -1377,12 +1310,12 @@ function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strok
1377
1310
  target.element.style.filter = '';
1378
1311
  target.element.style.left = `${vector.x * size / 4}px`;
1379
1312
  target.element.style.top = `${vector.y * size / 4}px`;
1380
- xnew$1.emit('-up', { type: '-up', vector });
1313
+ xnew$1.emit('-up', { vector });
1381
1314
  });
1382
1315
  }
1383
1316
  function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1384
1317
  xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1385
- xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1318
+ xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; user-select: none; pointer-events: auto; overflow: hidden;">`);
1386
1319
  const polygons = [
1387
1320
  '<polygon points="32 32 23 23 23 4 24 3 40 3 41 4 41 23">',
1388
1321
  '<polygon points="32 32 23 41 23 60 24 61 40 61 41 60 41 41">',
@@ -1442,6 +1375,8 @@ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity =
1442
1375
  });
1443
1376
  }
1444
1377
 
1378
+ const currentColorA = 'color-mix(in srgb, currentColor 70%, transparent)';
1379
+ const currentColorB = 'color-mix(in srgb, currentColor 10%, transparent)';
1445
1380
  function Panel(unit, { name, open = false, params }) {
1446
1381
  const object = params !== null && params !== void 0 ? params : {};
1447
1382
  xnew$1.extend(Group, { name, open });
@@ -1486,9 +1421,9 @@ function Panel(unit, { name, open = false, params }) {
1486
1421
  function Group(group, { name, open = false }) {
1487
1422
  xnew$1.extend(OpenAndClose, { open });
1488
1423
  if (name) {
1489
- xnew$1('<div style="height: 2rem; margin: 0.125rem 0; display: flex; align-items: center; cursor: pointer; user-select: none;">', (unit) => {
1424
+ xnew$1('<div style="height: 2em; margin: 0.125em 0; display: flex; align-items: center; cursor: pointer; user-select: none;">', (unit) => {
1490
1425
  unit.on('click', () => group.toggle());
1491
- xnew$1('<svg viewBox="0 0 12 12" style="width: 1rem; height: 1rem; margin-right: 0.25rem;" fill="none" stroke="currentColor">', (unit) => {
1426
+ xnew$1('<svg viewBox="0 0 12 12" style="width: 1em; height: 1em; margin-right: 0.25em;" fill="none" stroke="currentColor">', (unit) => {
1492
1427
  xnew$1('<path d="M6 2 10 6 6 10" />');
1493
1428
  group.on('-transition', ({ state }) => unit.element.style.transform = `rotate(${state * 90}deg)`);
1494
1429
  });
@@ -1498,11 +1433,11 @@ function Group(group, { name, open = false }) {
1498
1433
  xnew$1.extend(Accordion);
1499
1434
  }
1500
1435
  function Button(unit, { key = '' }) {
1501
- xnew$1.nest('<button style="margin: 0.125rem; height: 2rem; border: 1px solid; border-radius: 0.25rem; cursor: pointer;">');
1436
+ xnew$1.nest('<button style="margin: 0.125em 0; height: 2em; border: 1px solid; border-radius: 0.25em; cursor: pointer;">');
1502
1437
  unit.element.textContent = key;
1503
1438
  unit.on('pointerover', () => {
1504
- unit.element.style.background = 'color-mix(in srgb, currentColor 5%, transparent)';
1505
- unit.element.style.borderColor = 'color-mix(in srgb, currentColor 40%, transparent)';
1439
+ unit.element.style.background = currentColorB;
1440
+ unit.element.style.borderColor = currentColorA;
1506
1441
  });
1507
1442
  unit.on('pointerout', () => {
1508
1443
  unit.element.style.background = '';
@@ -1516,16 +1451,16 @@ function Button(unit, { key = '' }) {
1516
1451
  });
1517
1452
  }
1518
1453
  function Separator(unit) {
1519
- xnew$1.nest('<div style="margin: 0.5rem 0; border-top: 1px solid color-mix(in srgb, currentColor 40%, transparent);">');
1454
+ xnew$1.nest(`<div style="margin: 0.5em 0; border-top: 1px solid ${currentColorA};">`);
1520
1455
  }
1521
1456
  function Range(unit, { key = '', value, min = 0, max = 100, step = 1 }) {
1522
1457
  value = value !== null && value !== void 0 ? value : min;
1523
- xnew$1.nest(`<div style="position: relative; height: 2rem; margin: 0.125rem 0; cursor: pointer; user-select: none;">`);
1458
+ xnew$1.nest(`<div style="position: relative; height: 2em; margin: 0.125em 0; cursor: pointer; user-select: none;">`);
1524
1459
  // fill bar
1525
1460
  const ratio = (value - min) / (max - min);
1526
- const fill = xnew$1(`<div style="position: absolute; top: 0; left: 0; bottom: 0; width: ${ratio * 100}%; background: color-mix(in srgb, currentColor 5%, transparent); border: 1px solid color-mix(in srgb, currentColor 40%, transparent); border-radius: 0.25rem; transition: width 0.05s;">`);
1461
+ const fill = xnew$1(`<div style="position: absolute; top: 0; left: 0; bottom: 0; width: ${ratio * 100}%; background: ${currentColorB}; border: 1px solid ${currentColorA}; border-radius: 0.25em; transition: width 0.05s;">`);
1527
1462
  // overlay labels
1528
- const status = xnew$1('<div style="position: absolute; inset: 0; padding: 0 0.25rem; display: flex; justify-content: space-between; align-items: center; pointer-events: none;">', (unit) => {
1463
+ const status = xnew$1('<div style="position: absolute; inset: 0; padding: 0 0.5em; display: flex; justify-content: space-between; align-items: center; pointer-events: none;">', (unit) => {
1529
1464
  xnew$1('<div>', key);
1530
1465
  xnew$1('<div key="status">', value);
1531
1466
  });
@@ -1539,16 +1474,16 @@ function Range(unit, { key = '', value, min = 0, max = 100, step = 1 }) {
1539
1474
  });
1540
1475
  }
1541
1476
  function Checkbox(unit, { key = '', value } = {}) {
1542
- xnew$1.nest(`<div style="position: relative; height: 2rem; margin: 0.125rem 0; padding: 0 0.25rem; display: flex; align-items: center; cursor: pointer; user-select: none;">`);
1477
+ xnew$1.nest(`<div style="position: relative; height: 2em; margin: 0.125em 0; padding: 0 0.5em; display: flex; align-items: center; cursor: pointer; user-select: none;">`);
1543
1478
  xnew$1('<div style="flex: 1;">', key);
1544
- const box = xnew$1('<div style="width: 1.25rem; height: 1.25rem; border: 1px solid color-mix(in srgb, currentColor 40%, transparent); border-radius: 0.25rem; display: flex; align-items: center; justify-content: center; transition: background 0.1s;">', () => {
1545
- xnew$1('<svg viewBox="0 0 12 12" style="width: 1.25rem; height: 1.25rem; opacity: 0; transition: opacity 0.1s;" fill="none" stroke="color-mix(in srgb, currentColor 80%, transparent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">', () => {
1479
+ const box = xnew$1(`<div style="width: 1.25em; height: 1.25em; border: 1px solid ${currentColorA}; border-radius: 0.25em; display: flex; align-items: center; justify-content: center; transition: background 0.1s;">`, () => {
1480
+ xnew$1(`<svg viewBox="0 0 12 12" style="width: 1.25em; height: 1.25em; opacity: 0; transition: opacity 0.1s;" fill="none" stroke="${currentColorA}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">`, () => {
1546
1481
  xnew$1('<path d="M2 6 5 9 10 3" />');
1547
1482
  });
1548
1483
  });
1549
1484
  const check = box.element.querySelector('svg');
1550
1485
  const update = (checked) => {
1551
- box.element.style.background = checked ? 'color-mix(in srgb, currentColor 5%, transparent)' : '';
1486
+ box.element.style.background = checked ? currentColorB : '';
1552
1487
  check.style.opacity = checked ? '1' : '0';
1553
1488
  };
1554
1489
  update(!!value);
@@ -1557,12 +1492,59 @@ function Checkbox(unit, { key = '', value } = {}) {
1557
1492
  update(value);
1558
1493
  });
1559
1494
  }
1560
- function Select(unit, { key = '', value, options = [] } = {}) {
1561
- xnew$1.nest(`<div style="height: 2rem; margin: 0.125rem 0; padding: 0 0.25rem; display: flex; align-items: center;">`);
1495
+ function Select(_, { key = '', value, options = [] } = {}) {
1496
+ var _a;
1497
+ const initial = (_a = value !== null && value !== void 0 ? value : options[0]) !== null && _a !== void 0 ? _a : '';
1498
+ xnew$1.nest(`<div style="position: relative; height: 2em; margin: 0.125em 0; padding: 0 0.5em; display: flex; align-items: center;">`);
1562
1499
  xnew$1('<div style="flex: 1;">', key);
1563
- xnew$1.nest(`<select name="${key}" style="height: 2rem; padding: 0.25rem; border: 1px solid color-mix(in srgb, currentColor 40%, transparent); border-radius: 0.25rem; cursor: pointer; user-select: none;">`);
1564
- for (const option of options) {
1565
- xnew$1(`<option value="${option}" ${option === value ? 'selected' : ''}>`, option);
1500
+ const native = xnew$1(`<select name="${key}" style="display: none;">`, () => {
1501
+ for (const option of options) {
1502
+ xnew$1(`<option value="${option}" ${option === initial ? 'selected' : ''}>`, option);
1503
+ }
1504
+ });
1505
+ const button = xnew$1(`<div style="height: 2em; padding: 0 1.5em 0 0.5em; display: flex; align-items: center; border: 1px solid ${currentColorA}; border-radius: 0.25em; cursor: pointer; user-select: none; min-width: 3em; white-space: nowrap;">`, initial);
1506
+ xnew$1(`<svg viewBox="0 0 12 12" style="position: absolute; right: 1.0em; width: 0.75em; height: 0.75em; pointer-events: none;" fill="none" stroke="${currentColorA}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">`, () => {
1507
+ xnew$1('<path d="M2 4 6 8 10 4" />');
1508
+ });
1509
+ button.on('click', () => {
1510
+ xnew$1((list) => {
1511
+ xnew$1(OpenAndClose, { open: false });
1512
+ xnew$1.extend(Popup);
1513
+ xnew$1.nest('<div style="position: absolute; padding: 0.25em 0;">');
1514
+ list.on('render', () => {
1515
+ const rect = button.element.getBoundingClientRect();
1516
+ list.element.style.right = (window.innerWidth - rect.right) + 'px';
1517
+ list.element.style.top = rect.bottom + 'px';
1518
+ list.element.style.background = getEffectiveBg(button.element);
1519
+ });
1520
+ xnew$1.extend(Accordion);
1521
+ xnew$1.nest(`<div style="position: relative; border: 1px solid ${currentColorA}; border-radius: 0.25em; overflow: hidden;">`);
1522
+ for (const option of options) {
1523
+ const item = xnew$1(`<div style="height: 2em; padding: 0 0.5em; display: flex; align-items: center; cursor: pointer; user-select: none;">`, option);
1524
+ item.on('pointerover', () => item.element.style.background = currentColorB);
1525
+ item.on('pointerout', () => item.element.style.background = '');
1526
+ item.on('click', () => {
1527
+ button.element.textContent = option;
1528
+ native.element.value = option;
1529
+ native.element.dispatchEvent(new Event('input', { bubbles: false }));
1530
+ list.finalize();
1531
+ });
1532
+ }
1533
+ list.on('click.outside', () => {
1534
+ list.finalize();
1535
+ });
1536
+ });
1537
+ });
1538
+ xnew$1.nest(native.element);
1539
+ function getEffectiveBg(el) {
1540
+ let current = el.parentElement;
1541
+ while (current) {
1542
+ const bg = getComputedStyle(current).backgroundColor;
1543
+ if (bg && bg !== 'rgba(0, 0, 0, 0)' && bg !== 'transparent')
1544
+ return bg;
1545
+ current = current.parentElement;
1546
+ }
1547
+ return 'Canvas';
1566
1548
  }
1567
1549
  }
1568
1550
 
@@ -1797,7 +1779,7 @@ const basics = {
1797
1779
  DPad,
1798
1780
  Panel,
1799
1781
  Accordion,
1800
- Modal,
1782
+ Popup,
1801
1783
  };
1802
1784
  const audio = {
1803
1785
  load(path) {