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