@mulsense/xnew 0.5.4 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/xnew.mjs CHANGED
@@ -123,7 +123,8 @@ class AnimationTicker {
123
123
  class Timer {
124
124
  constructor(options) {
125
125
  this.options = options;
126
- this.id = null;
126
+ this.startid = null;
127
+ this.endid = null;
127
128
  this.time = 0.0;
128
129
  this.counter = 0;
129
130
  this.offset = 0.0;
@@ -147,19 +148,26 @@ class Timer {
147
148
  });
148
149
  this.visibilitychange = () => document.hidden === false ? this._start() : this._stop();
149
150
  document.addEventListener('visibilitychange', this.visibilitychange);
150
- // this.options.transition?.(0.0);
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);
151
155
  this.start();
152
156
  }
153
157
  clear() {
154
- if (this.id !== null) {
155
- clearTimeout(this.id);
156
- this.id = null;
158
+ if (this.startid !== null) {
159
+ clearTimeout(this.startid);
160
+ this.startid = null;
161
+ }
162
+ if (this.endid !== null) {
163
+ clearTimeout(this.endid);
164
+ this.endid = null;
157
165
  }
158
166
  document.removeEventListener('visibilitychange', this.visibilitychange);
159
167
  this.ticker.clear();
160
168
  }
161
169
  elapsed() {
162
- return this.offset + (this.id !== null ? (Date.now() - this.time) : 0);
170
+ return this.offset + (this.endid !== null ? (Date.now() - this.time) : 0);
163
171
  }
164
172
  start() {
165
173
  this.status = 1;
@@ -170,12 +178,12 @@ class Timer {
170
178
  this.status = 0;
171
179
  }
172
180
  _start() {
173
- if (this.status === 1 && this.id === null) {
174
- this.id = setTimeout(() => {
181
+ if (this.status === 1 && this.endid === null) {
182
+ this.endid = setTimeout(() => {
175
183
  var _a, _b, _c, _d;
176
184
  (_b = (_a = this.options).transition) === null || _b === void 0 ? void 0 : _b.call(_a, 1.0);
177
185
  (_d = (_c = this.options).timeout) === null || _d === void 0 ? void 0 : _d.call(_c);
178
- this.id = null;
186
+ this.endid = null;
179
187
  this.time = 0.0;
180
188
  this.offset = 0.0;
181
189
  this.counter++;
@@ -190,10 +198,10 @@ class Timer {
190
198
  }
191
199
  }
192
200
  _stop() {
193
- if (this.status === 1 && this.id !== null) {
201
+ if (this.status === 1 && this.endid !== null) {
194
202
  this.offset = this.offset + Date.now() - this.time;
195
- clearTimeout(this.id);
196
- this.id = null;
203
+ clearTimeout(this.endid);
204
+ this.endid = null;
197
205
  this.time = 0.0;
198
206
  }
199
207
  }
@@ -209,6 +217,12 @@ class Eventor {
209
217
  if (props.type === 'resize') {
210
218
  finalize = this.resize(props);
211
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
+ }
212
226
  else if (props.type === 'wheel') {
213
227
  finalize = this.wheel(props);
214
228
  }
@@ -242,6 +256,9 @@ class Eventor {
242
256
  else if (['keydown.arrow', 'keyup.arrow'].includes(props.type)) {
243
257
  finalize = this.key_arrow(props);
244
258
  }
259
+ else if (['keydown.wasd', 'keyup.wasd'].includes(props.type)) {
260
+ finalize = this.key_wasd(props);
261
+ }
245
262
  else {
246
263
  finalize = this.basic(props);
247
264
  }
@@ -256,7 +273,7 @@ class Eventor {
256
273
  }
257
274
  basic(props) {
258
275
  const execute = (event) => {
259
- props.listener({ event, type: event.type });
276
+ props.listener({ event });
260
277
  };
261
278
  props.element.addEventListener(props.type, execute, props.options);
262
279
  return () => {
@@ -266,7 +283,7 @@ class Eventor {
266
283
  resize(props) {
267
284
  const observer = new ResizeObserver((entries) => {
268
285
  for (const entry of entries) {
269
- props.listener({ type: 'resize' });
286
+ props.listener({});
270
287
  break;
271
288
  }
272
289
  });
@@ -275,9 +292,47 @@ class Eventor {
275
292
  observer.unobserve(props.element);
276
293
  };
277
294
  }
295
+ change(props) {
296
+ const execute = (event) => {
297
+ let value = null;
298
+ if (event.target.type === 'checkbox') {
299
+ value = event.target.checked;
300
+ }
301
+ else if (event.target.type === 'range' || event.target.type === 'number') {
302
+ value = parseFloat(event.target.value);
303
+ }
304
+ else {
305
+ value = event.target.value;
306
+ }
307
+ props.listener({ event, value });
308
+ };
309
+ props.element.addEventListener(props.type, execute, props.options);
310
+ return () => {
311
+ props.element.removeEventListener(props.type, execute);
312
+ };
313
+ }
314
+ input(props) {
315
+ const execute = (event) => {
316
+ let value = null;
317
+ if (event.target.type === 'checkbox') {
318
+ value = event.target.checked;
319
+ }
320
+ else if (event.target.type === 'range' || event.target.type === 'number') {
321
+ value = parseFloat(event.target.value);
322
+ }
323
+ else {
324
+ value = event.target.value;
325
+ }
326
+ props.listener({ event, value });
327
+ };
328
+ props.element.addEventListener(props.type, execute, props.options);
329
+ return () => {
330
+ props.element.removeEventListener(props.type, execute);
331
+ };
332
+ }
278
333
  click(props) {
279
334
  const execute = (event) => {
280
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
335
+ props.listener({ event, position: pointer(props.element, event).position });
281
336
  };
282
337
  props.element.addEventListener(props.type, execute, props.options);
283
338
  return () => {
@@ -287,7 +342,7 @@ class Eventor {
287
342
  click_outside(props) {
288
343
  const execute = (event) => {
289
344
  if (props.element.contains(event.target) === false) {
290
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
345
+ props.listener({ event, position: pointer(props.element, event).position });
291
346
  }
292
347
  };
293
348
  document.addEventListener(props.type.split('.')[0], execute, props.options);
@@ -297,7 +352,7 @@ class Eventor {
297
352
  }
298
353
  pointer(props) {
299
354
  const execute = (event) => {
300
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
355
+ props.listener({ event, position: pointer(props.element, event).position });
301
356
  };
302
357
  props.element.addEventListener(props.type, execute, props.options);
303
358
  return () => {
@@ -306,7 +361,7 @@ class Eventor {
306
361
  }
307
362
  mouse(props) {
308
363
  const execute = (event) => {
309
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
364
+ props.listener({ event, position: pointer(props.element, event).position });
310
365
  };
311
366
  props.element.addEventListener(props.type, execute, props.options);
312
367
  return () => {
@@ -315,7 +370,7 @@ class Eventor {
315
370
  }
316
371
  touch(props) {
317
372
  const execute = (event) => {
318
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
373
+ props.listener({ event, position: pointer(props.element, event).position });
319
374
  };
320
375
  props.element.addEventListener(props.type, execute, props.options);
321
376
  return () => {
@@ -325,7 +380,7 @@ class Eventor {
325
380
  pointer_outside(props) {
326
381
  const execute = (event) => {
327
382
  if (props.element.contains(event.target) === false) {
328
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
383
+ props.listener({ event, position: pointer(props.element, event).position });
329
384
  }
330
385
  };
331
386
  document.addEventListener(props.type.split('.')[0], execute, props.options);
@@ -335,7 +390,7 @@ class Eventor {
335
390
  }
336
391
  wheel(props) {
337
392
  const execute = (event) => {
338
- props.listener({ event, type: props.type, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } });
393
+ props.listener({ event, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } });
339
394
  };
340
395
  props.element.addEventListener(props.type, execute, props.options);
341
396
  return () => {
@@ -355,7 +410,7 @@ class Eventor {
355
410
  const position = pointer(props.element, event).position;
356
411
  const delta = { x: position.x - previous.x, y: position.y - previous.y };
357
412
  if (props.type === 'dragmove') {
358
- props.listener({ event, type: props.type, position, delta });
413
+ props.listener({ event, position, delta });
359
414
  }
360
415
  previous = position;
361
416
  }
@@ -364,7 +419,7 @@ class Eventor {
364
419
  if (event.pointerId === id) {
365
420
  const position = pointer(props.element, event).position;
366
421
  if (props.type === 'dragend') {
367
- props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
422
+ props.listener({ event, position, delta: { x: 0, y: 0 } });
368
423
  }
369
424
  remove();
370
425
  }
@@ -373,7 +428,7 @@ class Eventor {
373
428
  if (event.pointerId === id) {
374
429
  const position = pointer(props.element, event).position;
375
430
  if (props.type === 'dragend') {
376
- props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
431
+ props.listener({ event, position, delta: { x: 0, y: 0 } });
377
432
  }
378
433
  remove();
379
434
  }
@@ -382,7 +437,7 @@ class Eventor {
382
437
  window.addEventListener('pointerup', pointerup);
383
438
  window.addEventListener('pointercancel', pointercancel);
384
439
  if (props.type === 'dragstart') {
385
- props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
440
+ props.listener({ event, position, delta: { x: 0, y: 0 } });
386
441
  }
387
442
  };
388
443
  function remove() {
@@ -411,7 +466,7 @@ class Eventor {
411
466
  map.set(event.pointerId, position);
412
467
  isActive = map.size === 2 ? true : false;
413
468
  if (isActive === true && props.type === 'gesturestart') {
414
- props.listener({ event, type: props.type });
469
+ props.listener({ event });
415
470
  }
416
471
  };
417
472
  const dragmove = ({ event, position, delta }) => {
@@ -438,7 +493,7 @@ class Eventor {
438
493
  // }
439
494
  // }
440
495
  if (props.type === 'gesturemove') {
441
- props.listener({ event, type: props.type, scale });
496
+ props.listener({ event, scale });
442
497
  }
443
498
  }
444
499
  map.set(event.pointerId, position);
@@ -446,7 +501,7 @@ class Eventor {
446
501
  const dragend = ({ event }) => {
447
502
  map.delete(event.pointerId);
448
503
  if (isActive === true && props.type === 'gestureend') {
449
- props.listener({ event, type: props.type, scale: 1.0 });
504
+ props.listener({ event, scale: 1.0 });
450
505
  }
451
506
  isActive = false;
452
507
  };
@@ -470,7 +525,7 @@ class Eventor {
470
525
  const execute = (event) => {
471
526
  if (props.type === 'keydown' && event.repeat)
472
527
  return;
473
- props.listener({ event, type: props.type, code: event.code });
528
+ props.listener({ event, code: event.code });
474
529
  };
475
530
  window.addEventListener(props.type, execute, props.options);
476
531
  return () => {
@@ -488,7 +543,7 @@ class Eventor {
488
543
  x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
489
544
  y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
490
545
  };
491
- props.listener({ event, type: props.type, code: event.code, vector });
546
+ props.listener({ event, code: event.code, vector });
492
547
  }
493
548
  };
494
549
  const keyup = (event) => {
@@ -498,7 +553,38 @@ class Eventor {
498
553
  x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
499
554
  y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
500
555
  };
501
- props.listener({ event, type: props.type, code: event.code, vector });
556
+ props.listener({ event, code: event.code, vector });
557
+ }
558
+ };
559
+ window.addEventListener('keydown', keydown, props.options);
560
+ window.addEventListener('keyup', keyup, props.options);
561
+ return () => {
562
+ window.removeEventListener('keydown', keydown);
563
+ window.removeEventListener('keyup', keyup);
564
+ };
565
+ }
566
+ key_wasd(props) {
567
+ const keymap = {};
568
+ const keydown = (event) => {
569
+ if (event.repeat)
570
+ return;
571
+ keymap[event.code] = 1;
572
+ if (props.type === 'keydown.wasd' && ['KeyW', 'KeyA', 'KeyS', 'KeyD'].includes(event.code)) {
573
+ const vector = {
574
+ x: (keymap['KeyA'] ? -1 : 0) + (keymap['KeyD'] ? +1 : 0),
575
+ y: (keymap['KeyW'] ? -1 : 0) + (keymap['KeyS'] ? +1 : 0)
576
+ };
577
+ props.listener({ event, code: event.code, vector });
578
+ }
579
+ };
580
+ const keyup = (event) => {
581
+ keymap[event.code] = 0;
582
+ if (props.type === 'keyup.wasd' && ['KeyW', 'KeyA', 'KeyS', 'KeyD'].includes(event.code)) {
583
+ const vector = {
584
+ x: (keymap['KeyA'] ? -1 : 0) + (keymap['KeyD'] ? +1 : 0),
585
+ y: (keymap['KeyW'] ? -1 : 0) + (keymap['KeyS'] ? +1 : 0)
586
+ };
587
+ props.listener({ event, code: event.code, vector });
502
588
  }
503
589
  };
504
590
  window.addEventListener('keydown', keydown, props.options);
@@ -627,7 +713,7 @@ class Unit {
627
713
  baseComponent = (unit) => { };
628
714
  }
629
715
  const baseContext = (_a = parent === null || parent === void 0 ? void 0 : parent._.currentContext) !== null && _a !== void 0 ? _a : { stack: null };
630
- this._ = { parent, target, baseElement, baseContext, baseComponent, props };
716
+ this._ = { parent, target, baseElement, baseContext, baseComponent, props: props !== null && props !== void 0 ? props : {} };
631
717
  parent === null || parent === void 0 ? void 0 : parent._.children.push(this);
632
718
  Unit.initialize(this, null);
633
719
  }
@@ -670,7 +756,7 @@ class Unit {
670
756
  children: [],
671
757
  elements: [],
672
758
  promises: [],
673
- extends: [],
759
+ components: [],
674
760
  listeners: new MapMap(),
675
761
  defines: {},
676
762
  systems: { start: [], update: [], render: [], stop: [], finalize: [] },
@@ -689,10 +775,10 @@ class Unit {
689
775
  static finalize(unit) {
690
776
  if (unit._.state !== 'finalized' && unit._.state !== 'finalizing') {
691
777
  unit._.state = 'finalizing';
692
- unit._.children.forEach((child) => child.finalize());
693
- unit._.systems.finalize.forEach(({ execute }) => execute());
778
+ [...unit._.children].reverse().forEach((child) => child.finalize());
779
+ [...unit._.systems.finalize].reverse().forEach(({ execute }) => execute());
694
780
  unit.off();
695
- unit._.extends.forEach(({ component }) => Unit.component2units.delete(component, unit));
781
+ unit._.components.forEach((component) => Unit.component2units.delete(component, unit));
696
782
  if (unit._.elements.length > 0) {
697
783
  unit._.baseElement.removeChild(unit._.elements[0]);
698
784
  unit._.currentElement = unit._.baseElement;
@@ -731,8 +817,7 @@ class Unit {
731
817
  }
732
818
  static extend(unit, component, props) {
733
819
  var _a;
734
- const find = unit._.extends.find(({ component: c }) => c === component);
735
- if (find !== undefined) {
820
+ if (unit._.components.includes(component) === true) {
736
821
  throw new Error(`The component is already extended.`);
737
822
  }
738
823
  else {
@@ -740,6 +825,8 @@ class Unit {
740
825
  unit._.currentComponent = component;
741
826
  const defines = (_a = component(unit, props)) !== null && _a !== void 0 ? _a : {};
742
827
  unit._.currentComponent = backupComponent;
828
+ Unit.component2units.add(component, unit);
829
+ unit._.components.push(component);
743
830
  Object.keys(defines).forEach((key) => {
744
831
  if (unit[key] !== undefined && unit._.defines[key] === undefined) {
745
832
  throw new Error(`The property "${key}" already exists.`);
@@ -756,15 +843,12 @@ class Unit {
756
843
  else if (typeof (descriptor === null || descriptor === void 0 ? void 0 : descriptor.value) === 'function') {
757
844
  wrapper.value = (...args) => Unit.scope(snapshot, descriptor.value, ...args);
758
845
  }
759
- else if ((descriptor === null || descriptor === void 0 ? void 0 : descriptor.value) !== undefined) {
760
- wrapper.get = () => defines[key];
761
- wrapper.set = (value) => defines[key] = value;
846
+ else {
847
+ throw new Error(`Only function properties can be defined as component defines. [${key}]`);
762
848
  }
763
849
  Object.defineProperty(unit._.defines, key, wrapper);
764
850
  Object.defineProperty(unit, key, wrapper);
765
851
  });
766
- Unit.component2units.add(component, unit);
767
- unit._.extends.push({ component, defines });
768
852
  return defines;
769
853
  }
770
854
  }
@@ -861,7 +945,9 @@ class Unit {
861
945
  }
862
946
  static on(unit, type, listener, options) {
863
947
  const snapshot = Unit.snapshot(Unit.currentUnit);
864
- const execute = (...args) => Unit.scope(snapshot, listener, ...args);
948
+ const execute = (props) => {
949
+ Unit.scope(snapshot, listener, Object.assign({ type }, props));
950
+ };
865
951
  if (SYSTEM_EVENTS.includes(type)) {
866
952
  unit._.systems[type].push({ listener, execute });
867
953
  }
@@ -890,7 +976,7 @@ class Unit {
890
976
  Unit.type2units.delete(type, unit);
891
977
  }
892
978
  }
893
- static emit(type, ...args) {
979
+ static emit(type, props = {}) {
894
980
  var _a, _b;
895
981
  const current = Unit.currentUnit;
896
982
  if (type[0] === '+') {
@@ -898,12 +984,12 @@ class Unit {
898
984
  var _a;
899
985
  const find = [unit, ...unit._.ancestors].find(u => u._.protected === true);
900
986
  if (find === undefined || current._.ancestors.includes(find) === true || current === find) {
901
- (_a = unit._.listeners.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((item) => item.execute(...args));
987
+ (_a = unit._.listeners.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((item) => item.execute(props));
902
988
  }
903
989
  });
904
990
  }
905
991
  else if (type[0] === '-') {
906
- (_b = current._.listeners.get(type)) === null || _b === void 0 ? void 0 : _b.forEach((item) => item.execute(...args));
992
+ (_b = current._.listeners.get(type)) === null || _b === void 0 ? void 0 : _b.forEach((item) => item.execute(props));
907
993
  }
908
994
  }
909
995
  }
@@ -929,23 +1015,27 @@ const xnew$1 = Object.assign(function (...args) {
929
1015
  }
930
1016
  const component = args.shift();
931
1017
  const props = args.shift();
932
- return new Unit(Unit.currentUnit, target, component, props);
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;
933
1023
  }, {
934
1024
  /**
935
1025
  * Creates a nested HTML/SVG element within the current component
936
- * @param tag - HTML or SVG tag name (e.g., '<div>', '<span>', '<svg>')
1026
+ * @param tag - HTML or SVG tag string (e.g., '<div class="my-class">', '<span style="color:red">', '<svg viewBox="0 0 24 24">')
937
1027
  * @returns The created HTML/SVG element
938
1028
  * @throws Error if called after component initialization
939
1029
  * @example
940
1030
  * const div = xnew.nest('<div>')
941
1031
  * div.textContent = 'Hello'
942
1032
  */
943
- nest(tag, textContent) {
1033
+ nest(tag) {
944
1034
  try {
945
1035
  if (Unit.currentUnit._.state !== 'invoked') {
946
1036
  throw new Error('xnew.nest can not be called after initialized.');
947
1037
  }
948
- return Unit.nest(Unit.currentUnit, tag, textContent);
1038
+ return Unit.nest(Unit.currentUnit, tag);
949
1039
  }
950
1040
  catch (error) {
951
1041
  console.error('xnew.nest(tag: string): ', error);
@@ -966,7 +1056,11 @@ const xnew$1 = Object.assign(function (...args) {
966
1056
  if (Unit.currentUnit._.state !== 'invoked') {
967
1057
  throw new Error('xnew.extend can not be called after initialized.');
968
1058
  }
969
- return Unit.extend(Unit.currentUnit, component, props);
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;
970
1064
  }
971
1065
  catch (error) {
972
1066
  console.error('xnew.extend(component: Function, props?: Object): ', error);
@@ -974,23 +1068,23 @@ const xnew$1 = Object.assign(function (...args) {
974
1068
  }
975
1069
  },
976
1070
  /**
977
- * Gets or sets a context value that can be accessed by child components
978
- * @param key - Context key
979
- * @param value - Optional value to set (if undefined, gets the value)
980
- * @returns The context value if getting, undefined if setting
1071
+ * Gets a context value that can be accessed in follow context
1072
+ * @param component - component function
1073
+ * @returns The context value
981
1074
  * @example
982
- * // Set context in parent
983
- * xnew.context('theme', 'dark')
1075
+ * // Create unit
1076
+ * const a = xnew(A);
1077
+ * ------------------------------
984
1078
  *
985
1079
  * // Get context in child
986
- * const theme = xnew.context('theme')
1080
+ * const a = xnew.context(A)
987
1081
  */
988
- context(key, value = undefined) {
1082
+ context(component) {
989
1083
  try {
990
- return Unit.context(Unit.currentUnit, key, value);
1084
+ return Unit.context(Unit.currentUnit, component);
991
1085
  }
992
1086
  catch (error) {
993
- console.error('xnew.context(key: string, value?: any): ', error);
1087
+ console.error('xnew.context(component: Function): ', error);
994
1088
  throw error;
995
1089
  }
996
1090
  },
@@ -1173,8 +1267,8 @@ const xnew$1 = Object.assign(function (...args) {
1173
1267
  },
1174
1268
  });
1175
1269
 
1176
- function OpenAndClose(unit, { state: initialState = 0.0 } = {}) {
1177
- let state = Math.max(0.0, Math.min(1.0, initialState));
1270
+ function OpenAndClose(unit, { open = false } = {}) {
1271
+ let state = open ? 1.0 : 0.0;
1178
1272
  let direction = state === 1.0 ? +1 : (state === 0.0 ? -1 : null);
1179
1273
  let timer = xnew$1.timeout(() => xnew$1.emit('-transition', { state }));
1180
1274
  return {
@@ -1194,10 +1288,10 @@ function OpenAndClose(unit, { state: initialState = 0.0 } = {}) {
1194
1288
  timer = xnew$1.transition((x) => {
1195
1289
  const y = x < 1.0 ? (1 - x) * d : 0.0;
1196
1290
  state = 1.0 - y;
1197
- xnew$1.emit('-transition', { state, type: '-transition' });
1291
+ xnew$1.emit('-transition', { state });
1198
1292
  }, duration * d, easing)
1199
1293
  .timeout(() => {
1200
- xnew$1.emit('-opened', { state, type: '-opened' });
1294
+ xnew$1.emit('-opened', { state });
1201
1295
  });
1202
1296
  }
1203
1297
  },
@@ -1209,128 +1303,105 @@ function OpenAndClose(unit, { state: initialState = 0.0 } = {}) {
1209
1303
  timer = xnew$1.transition((x) => {
1210
1304
  const y = x < 1.0 ? (1 - x) * d : 0.0;
1211
1305
  state = y;
1212
- xnew$1.emit('-transition', { state, type: '-transition' });
1306
+ xnew$1.emit('-transition', { state });
1213
1307
  }, duration * d, easing)
1214
1308
  .timeout(() => {
1215
- xnew$1.emit('-closed', { state, type: '-closed' });
1309
+ xnew$1.emit('-closed', { state });
1216
1310
  });
1217
1311
  }
1218
1312
  },
1219
1313
  };
1220
1314
  }
1315
+ function Accordion(unit) {
1316
+ const system = xnew$1.context(OpenAndClose);
1317
+ const outer = xnew$1.nest('<div style="overflow: hidden;">');
1318
+ const inner = xnew$1.nest('<div style="display: flex; flex-direction: column; box-sizing: border-box;">');
1319
+ system.on('-transition', ({ state }) => {
1320
+ outer.style.height = state < 1.0 ? inner.offsetHeight * state + 'px' : 'auto';
1321
+ outer.style.opacity = state.toString();
1322
+ });
1323
+ }
1324
+ function Modal(unit, { background = 'rgba(0, 0, 0, 0.1)' } = {}) {
1325
+ const system = xnew$1.context(OpenAndClose);
1326
+ system.on('-closed', () => unit.finalize());
1327
+ xnew$1.nest('<div style="position: fixed; inset: 0; z-index: 1000;">');
1328
+ unit.on('click', ({ event }) => system.close());
1329
+ const outer = xnew$1.nest(`<div style="width: 100%; height: 100%; opacity: 0;"">`);
1330
+ xnew$1.nest('<div style="position: absolute; inset: 0; margin: auto; width: max-content; height: max-content;">');
1331
+ unit.on('click', ({ event }) => event.stopPropagation());
1332
+ outer.style.background = background;
1333
+ system.on('-transition', ({ state }) => {
1334
+ outer.style.opacity = state.toString();
1335
+ });
1336
+ }
1221
1337
 
1222
- function Screen(unit, { width, height, fit = 'contain' } = {}) {
1223
- const size = { width: width !== null && width !== void 0 ? width : 800, height: height !== null && height !== void 0 ? height : 600 };
1224
- const outer = xnew$1.nest('<div style="position: relative; width: 100%; height: 100%; overflow: hidden;">');
1225
- xnew$1().on('resize', resize);
1226
- const absolute = xnew$1.nest('<div style="position: absolute; margin: auto; container-type: size; overflow: hidden;">');
1227
- const canvas = xnew$1(`<canvas width="${size.width}" height="${size.height}" style="width: 100%; height: 100%; vertical-align: bottom; user-select: none; user-drag: none; pointer-events: auto;">`);
1228
- resize();
1229
- function resize() {
1230
- const style = { width: '100%', height: '100%', top: 0, left: 0, bottom: 0, right: 0 };
1231
- if (fit === 'contain') {
1232
- const aspect = size.width / size.height;
1233
- if (outer.clientWidth < outer.clientHeight * aspect) {
1234
- style.height = Math.floor(outer.clientWidth / aspect) + 'px';
1235
- }
1236
- else {
1237
- style.width = Math.floor(outer.clientHeight * aspect) + 'px';
1238
- }
1239
- }
1240
- else if (fit === 'cover') {
1241
- const aspect = size.width / size.height;
1242
- if (outer.clientWidth < outer.clientHeight * aspect) {
1243
- style.width = Math.floor(outer.clientHeight * aspect) + 'px';
1244
- style.left = Math.floor((outer.clientWidth - outer.clientHeight * aspect) / 2) + 'px';
1245
- style.right = 'auto';
1246
- }
1247
- else {
1248
- style.height = Math.floor(outer.clientWidth / aspect) + 'px';
1249
- style.top = Math.floor((outer.clientHeight - outer.clientWidth / aspect) / 2) + 'px';
1250
- style.bottom = 'auto';
1251
- }
1252
- }
1253
- else if (fit === 'resize') {
1254
- size.width = outer.clientWidth > 0 ? outer.clientWidth : size.width;
1255
- size.height = outer.clientHeight > 0 ? outer.clientHeight : size.height;
1256
- console.log(size);
1257
- canvas.element.setAttribute('width', size.width + 'px');
1258
- canvas.element.setAttribute('height', size.height + 'px');
1259
- }
1260
- Object.assign(absolute.style, style);
1338
+ function Screen(unit, { aspect, fit = 'contain' } = {}) {
1339
+ xnew$1.nest('<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size; overflow: hidden;">');
1340
+ xnew$1.nest(`<div style="position: relative; aspect-ratio: ${aspect}; container-type: size; overflow: hidden;">`);
1341
+ if (fit === 'contain') {
1342
+ unit.element.style.width = `min(100cqw, calc(100cqh * ${aspect}))`;
1343
+ }
1344
+ else {
1345
+ unit.element.style.flexShrink = '0';
1346
+ unit.element.style.width = `max(100cqw, calc(100cqh * ${aspect}))`;
1261
1347
  }
1262
- return {
1263
- get canvas() {
1264
- return canvas.element;
1265
- },
1266
- };
1267
1348
  }
1268
1349
 
1269
1350
  //----------------------------------------------------------------------------------------------------
1270
1351
  // controller
1271
1352
  //----------------------------------------------------------------------------------------------------
1272
- function SVGTemplate(self, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round', fill = null, fillOpacity = 0.8 }) {
1353
+ function SVGTemplate(self, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = null, fillOpacity = 0.8 }) {
1273
1354
  xnew$1.nest(`<svg
1274
- viewBox="0 0 100 100"
1355
+ viewBox="0 0 64 64"
1275
1356
  style="position: absolute; width: 100%; height: 100%; select: none;
1276
1357
  stroke: ${stroke}; stroke-opacity: ${strokeOpacity}; stroke-width: ${strokeWidth}; stroke-linejoin: ${strokeLinejoin};
1277
1358
  ${fill ? `fill: ${fill}; fill-opacity: ${fillOpacity};` : ''}
1278
1359
  ">`);
1279
1360
  }
1280
- function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1281
- const outer = xnew$1.nest(`<div style="position: relative; width: 100%; height: 100%;">`);
1282
- let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1283
- const inner = xnew$1.nest(`<div style="position: absolute; width: ${newsize}px; height: ${newsize}px; margin: auto; inset: 0; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1284
- xnew$1(outer).on('resize', () => {
1285
- newsize = Math.min(outer.clientWidth, outer.clientHeight);
1286
- inner.style.width = `${newsize}px`;
1287
- inner.style.height = `${newsize}px`;
1288
- });
1361
+ function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1362
+ xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1363
+ xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1289
1364
  xnew$1((unit) => {
1290
1365
  xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1291
- xnew$1('<polygon points="50 7 40 18 60 18">');
1292
- xnew$1('<polygon points="50 93 40 83 60 83">');
1293
- xnew$1('<polygon points=" 7 50 18 40 18 60">');
1294
- xnew$1('<polygon points="93 50 83 40 83 60">');
1366
+ xnew$1('<polygon points="32 7 27 13 37 13">');
1367
+ xnew$1('<polygon points="32 57 27 51 37 51">');
1368
+ xnew$1('<polygon points=" 7 32 13 27 13 37">');
1369
+ xnew$1('<polygon points="57 32 51 27 51 37">');
1295
1370
  });
1296
1371
  const target = xnew$1((unit) => {
1297
1372
  xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1298
- xnew$1('<circle cx="50" cy="50" r="23">');
1373
+ xnew$1('<circle cx="32" cy="32" r="14">');
1299
1374
  });
1300
1375
  unit.on('dragstart dragmove', ({ type, position }) => {
1301
- const x = position.x - newsize / 2;
1302
- const y = position.y - newsize / 2;
1303
- const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (newsize / 4));
1376
+ const size = unit.element.clientWidth;
1377
+ const x = position.x - size / 2;
1378
+ const y = position.y - size / 2;
1379
+ const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (size / 4));
1304
1380
  const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1305
1381
  const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1306
1382
  target.element.style.filter = 'brightness(80%)';
1307
- target.element.style.left = `${vector.x * newsize / 4}px`;
1308
- target.element.style.top = `${vector.y * newsize / 4}px`;
1383
+ target.element.style.left = `${vector.x * size / 4}px`;
1384
+ target.element.style.top = `${vector.y * size / 4}px`;
1309
1385
  const nexttype = { dragstart: '-down', dragmove: '-move' }[type];
1310
1386
  xnew$1.emit(nexttype, { type: nexttype, vector });
1311
1387
  });
1312
1388
  unit.on('dragend', () => {
1389
+ const size = unit.element.clientWidth;
1313
1390
  const vector = { x: 0, y: 0 };
1314
1391
  target.element.style.filter = '';
1315
- target.element.style.left = `${vector.x * newsize / 4}px`;
1316
- target.element.style.top = `${vector.y * newsize / 4}px`;
1392
+ target.element.style.left = `${vector.x * size / 4}px`;
1393
+ target.element.style.top = `${vector.y * size / 4}px`;
1317
1394
  xnew$1.emit('-up', { type: '-up', vector });
1318
1395
  });
1319
1396
  }
1320
- function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1321
- const outer = xnew$1.nest(`<div style="position: relative; width: 100%; height: 100%;">`);
1322
- let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1323
- const inner = xnew$1.nest(`<div style="position: absolute; width: ${newsize}px; height: ${newsize}px; margin: auto; inset: 0; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1324
- xnew$1(outer).on('resize', () => {
1325
- newsize = Math.min(outer.clientWidth, outer.clientHeight);
1326
- inner.style.width = `${newsize}px`;
1327
- inner.style.height = `${newsize}px`;
1328
- });
1397
+ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1398
+ xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1399
+ xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1329
1400
  const polygons = [
1330
- '<polygon points="50 50 35 35 35 5 37 3 63 3 65 5 65 35">',
1331
- '<polygon points="50 50 35 65 35 95 37 97 63 97 65 95 65 65">',
1332
- '<polygon points="50 50 35 35 5 35 3 37 3 63 5 65 35 65">',
1333
- '<polygon points="50 50 65 35 95 35 97 37 97 63 95 65 65 65">'
1401
+ '<polygon points="32 32 23 23 23 4 24 3 40 3 41 4 41 23">',
1402
+ '<polygon points="32 32 23 41 23 60 24 61 40 61 41 60 41 41">',
1403
+ '<polygon points="32 32 23 23 4 23 3 24 3 40 4 41 23 41">',
1404
+ '<polygon points="32 32 41 23 60 23 61 24 61 40 60 41 41 41">'
1334
1405
  ];
1335
1406
  const targets = polygons.map((polygon) => {
1336
1407
  return xnew$1((unit) => {
@@ -1340,20 +1411,21 @@ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity =
1340
1411
  });
1341
1412
  xnew$1((unit) => {
1342
1413
  xnew$1.extend(SVGTemplate, { fill: 'none', stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1343
- xnew$1('<polyline points="35 35 35 5 37 3 63 3 65 5 65 35">');
1344
- xnew$1('<polyline points="35 65 35 95 37 97 63 97 65 95 65 65">');
1345
- xnew$1('<polyline points="35 35 5 35 3 37 3 63 5 65 35 65">');
1346
- xnew$1('<polyline points="65 35 95 35 97 37 97 63 95 65 65 65">');
1347
- xnew$1('<polygon points="50 11 42 20 58 20">');
1348
- xnew$1('<polygon points="50 89 42 80 58 80">');
1349
- xnew$1('<polygon points="11 50 20 42 20 58">');
1350
- xnew$1('<polygon points="89 50 80 42 80 58">');
1414
+ xnew$1('<polyline points="23 23 23 4 24 3 40 3 41 4 41 23">');
1415
+ xnew$1('<polyline points="23 41 23 60 24 61 40 61 41 60 41 41">');
1416
+ xnew$1('<polyline points="23 23 4 23 3 24 3 40 4 41 23 41">');
1417
+ xnew$1('<polyline points="41 23 60 23 61 24 61 40 60 41 41 41">');
1418
+ xnew$1('<polygon points="32 7 27 13 37 13">');
1419
+ xnew$1('<polygon points="32 57 27 51 37 51">');
1420
+ xnew$1('<polygon points=" 7 32 13 27 13 37">');
1421
+ xnew$1('<polygon points="57 32 51 27 51 37">');
1351
1422
  });
1352
1423
  unit.on('dragstart dragmove', ({ type, position }) => {
1353
- const x = position.x - newsize / 2;
1354
- const y = position.y - newsize / 2;
1424
+ const size = unit.element.clientWidth;
1425
+ const x = position.x - size / 2;
1426
+ const y = position.y - size / 2;
1355
1427
  const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1356
- const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (newsize / 4));
1428
+ const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (size / 4));
1357
1429
  const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1358
1430
  if (diagonal === true) {
1359
1431
  vector.x = Math.abs(vector.x) > 0.5 ? Math.sign(vector.x) : 0;
@@ -1372,7 +1444,7 @@ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity =
1372
1444
  targets[2].element.style.filter = (vector.x < 0) ? 'brightness(80%)' : '';
1373
1445
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(80%)' : '';
1374
1446
  const nexttype = { dragstart: '-down', dragmove: '-move' }[type];
1375
- xnew$1.emit(nexttype, { type: nexttype, vector });
1447
+ xnew$1.emit(nexttype, { vector });
1376
1448
  });
1377
1449
  unit.on('dragend', () => {
1378
1450
  const vector = { x: 0, y: 0 };
@@ -1380,10 +1452,125 @@ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity =
1380
1452
  targets[1].element.style.filter = '';
1381
1453
  targets[2].element.style.filter = '';
1382
1454
  targets[3].element.style.filter = '';
1383
- xnew$1.emit('-up', { type: '-up', vector });
1455
+ xnew$1.emit('-up', { vector });
1384
1456
  });
1385
1457
  }
1386
1458
 
1459
+ function GUIPanel(unit, { name, open = false, params }) {
1460
+ const object = params !== null && params !== void 0 ? params : {};
1461
+ xnew$1.extend(Group, { name, open });
1462
+ return {
1463
+ group({ name, open, params }, inner) {
1464
+ const group = xnew$1((unit) => {
1465
+ xnew$1.extend(GUIPanel, { name, open, params: params !== null && params !== void 0 ? params : object });
1466
+ inner(unit);
1467
+ });
1468
+ group.on('-eventcapture', ({ event, key, value }) => {
1469
+ xnew$1.emit('-eventcapture', { event, key, value });
1470
+ });
1471
+ return group;
1472
+ },
1473
+ button(key) {
1474
+ const button = xnew$1(Button, { key });
1475
+ button.on('click', ({ event }) => {
1476
+ xnew$1.emit('-eventcapture', { event, key });
1477
+ });
1478
+ return button;
1479
+ },
1480
+ select(key, { options = [] } = {}) {
1481
+ var _a, _b;
1482
+ object[key] = (_b = (_a = object[key]) !== null && _a !== void 0 ? _a : options[0]) !== null && _b !== void 0 ? _b : '';
1483
+ const select = xnew$1(Select, { key, value: object[key], options });
1484
+ select.on('input', ({ event, value }) => {
1485
+ xnew$1.emit('-eventcapture', { event, key, value });
1486
+ });
1487
+ return select;
1488
+ },
1489
+ range(key, options = {}) {
1490
+ var _a;
1491
+ object[key] = (_a = object[key]) !== null && _a !== void 0 ? _a : 0;
1492
+ const number = xnew$1(Range, Object.assign({ key, value: object[key] }, options));
1493
+ number.on('input', ({ event, value }) => {
1494
+ object[key] = value;
1495
+ xnew$1.emit('-eventcapture', { event, key, value });
1496
+ });
1497
+ return number;
1498
+ },
1499
+ checkbox(key) {
1500
+ var _a;
1501
+ object[key] = (_a = object[key]) !== null && _a !== void 0 ? _a : false;
1502
+ const checkbox = xnew$1(Checkbox, { key, value: object[key] });
1503
+ checkbox.on('input', ({ event, value }) => {
1504
+ object[key] = value;
1505
+ xnew$1.emit('-eventcapture', { event, key, value });
1506
+ });
1507
+ return checkbox;
1508
+ },
1509
+ separator() {
1510
+ xnew$1(Separator);
1511
+ }
1512
+ };
1513
+ }
1514
+ function Group(group, { name, open = false }) {
1515
+ xnew$1.extend(OpenAndClose, { open });
1516
+ if (name) {
1517
+ xnew$1('<div style="display: flex; align-items: center; cursor: pointer;">', (unit) => {
1518
+ unit.on('click', () => group.toggle());
1519
+ xnew$1('<svg viewBox="0 0 12 12" style="width: 1rem; height: 1rem; margin-right: 0.25rem;" fill="none" stroke="currentColor">', (unit) => {
1520
+ xnew$1('<path d="M6 2 10 6 6 10" />');
1521
+ group.on('-transition', ({ state }) => unit.element.style.transform = `rotate(${state * 90}deg)`);
1522
+ });
1523
+ xnew$1('<div>', name);
1524
+ });
1525
+ }
1526
+ xnew$1.extend(Accordion);
1527
+ }
1528
+ function Button(unit, { key = '' }) {
1529
+ xnew$1.nest('<button style="margin: 0.125rem; padding: 0.125rem; border: 1px solid; border-radius: 0.25rem; cursor: pointer;">');
1530
+ unit.element.textContent = key;
1531
+ unit.on('pointerover', () => {
1532
+ unit.element.style.background = 'rgba(0, 0, 128, 0.1)';
1533
+ unit.element.style.borderColor = 'blue';
1534
+ });
1535
+ unit.on('pointerout', () => {
1536
+ unit.element.style.background = '';
1537
+ unit.element.style.borderColor = '';
1538
+ });
1539
+ unit.on('pointerdown', () => {
1540
+ unit.element.style.filter = 'brightness(0.5)';
1541
+ });
1542
+ unit.on('pointerup', () => {
1543
+ unit.element.style.filter = '';
1544
+ });
1545
+ }
1546
+ function Separator(unit) {
1547
+ xnew$1.nest('<div style="margin: 0.5rem 0; border-top: 1px solid;">');
1548
+ }
1549
+ function Range(unit, { key = '', value, min = 0, max = 100, step = 1 }) {
1550
+ xnew$1.nest(`<div style="margin: 0.125rem;">`);
1551
+ const status = xnew$1('<div style="display: flex; justify-content: space-between;">', (unit) => {
1552
+ xnew$1('<div style="flex: 1;">', key);
1553
+ xnew$1('<div key="status" style="flex: none;">', value);
1554
+ });
1555
+ xnew$1.nest(`<input type="range" name="${key}" min="${min}" max="${max}" step="${step}" value="${value}" style="width: 100%; cursor: pointer;">`);
1556
+ unit.on('input', ({ event }) => {
1557
+ status.element.querySelector('[key="status"]').textContent = event.target.value;
1558
+ });
1559
+ }
1560
+ function Checkbox(unit, { key = '', value } = {}) {
1561
+ xnew$1.nest(`<label style="margin: 0.125rem; display: flex; align-items: center; cursor: pointer;">`);
1562
+ xnew$1('<div style="flex: 1;">', key);
1563
+ xnew$1.nest(`<input type="checkbox" name="${key}" ${value ? 'checked' : ''} style="margin-right: 0.25rem;">`);
1564
+ }
1565
+ function Select(unit, { key = '', value, options = [] } = {}) {
1566
+ xnew$1.nest(`<div style="margin: 0.125rem; display: flex; align-items: center;">`);
1567
+ xnew$1('<div style="flex: 1;">', key);
1568
+ xnew$1.nest(`<select name="${key}" style="padding: 0.125rem; border: 1px solid; border-radius: 0.25rem; cursor: pointer;">`);
1569
+ for (const option of options) {
1570
+ xnew$1(`<option value="${option}" ${option === value ? 'selected' : ''}>`, option);
1571
+ }
1572
+ }
1573
+
1387
1574
  const context = new window.AudioContext();
1388
1575
  const master = context.createGain();
1389
1576
  //----------------------------------------------------------------------------------------------------
@@ -1613,6 +1800,9 @@ const basics = {
1613
1800
  OpenAndClose,
1614
1801
  AnalogStick,
1615
1802
  DPad,
1803
+ GUIPanel,
1804
+ Accordion,
1805
+ Modal,
1616
1806
  };
1617
1807
  const audio = {
1618
1808
  load(path) {