@mulsense/xnew 0.6.0 → 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.
@@ -28,7 +28,7 @@
28
28
  xnew(Root, { canvas });
29
29
  },
30
30
  nest(object) {
31
- xnew.extend(Nest, { object });
31
+ xnew(Nest, { object });
32
32
  return object;
33
33
  },
34
34
  get renderer() {
@@ -6,7 +6,7 @@ var xpixi = {
6
6
  xnew(Root, { canvas });
7
7
  },
8
8
  nest(object) {
9
- xnew.extend(Nest, { object });
9
+ xnew(Nest, { object });
10
10
  return object;
11
11
  },
12
12
  get renderer() {
@@ -6,7 +6,7 @@
6
6
 
7
7
  var xrapier2d = {
8
8
  initialize({ gravity = { x: 0.0, y: -9.81 } } = {}) {
9
- xnew.extend(Root, { gravity });
9
+ xnew(Root, { gravity });
10
10
  },
11
11
  get world() {
12
12
  var _a;
@@ -3,7 +3,7 @@ import RAPIER from '@dimforge/rapier2d-compat';
3
3
 
4
4
  var xrapier2d = {
5
5
  initialize({ gravity = { x: 0.0, y: -9.81 } } = {}) {
6
- xnew.extend(Root, { gravity });
6
+ xnew(Root, { gravity });
7
7
  },
8
8
  get world() {
9
9
  var _a;
@@ -68,6 +68,9 @@
68
68
  unit.on('finalize', () => {
69
69
  parent.remove(object);
70
70
  });
71
+ return {
72
+ get threeObject() { return object; },
73
+ };
71
74
  }
72
75
 
73
76
  return xthree;
@@ -46,6 +46,9 @@ function Nest(unit, { object }) {
46
46
  unit.on('finalize', () => {
47
47
  parent.remove(object);
48
48
  });
49
+ return {
50
+ get threeObject() { return object; },
51
+ };
49
52
  }
50
53
 
51
54
  export { xthree as default };
package/dist/xnew.d.ts CHANGED
@@ -34,6 +34,8 @@ declare class Eventor {
34
34
  remove(type: string, listener: Function): void;
35
35
  private basic;
36
36
  private resize;
37
+ private change;
38
+ private input;
37
39
  private click;
38
40
  private click_outside;
39
41
  private pointer;
@@ -147,7 +149,7 @@ declare class Unit {
147
149
  off(type?: string, listener?: Function): void;
148
150
  static on(unit: Unit, type: string, listener: Function, options?: boolean | AddEventListenerOptions): void;
149
151
  static off(unit: Unit, type: string, listener?: Function): void;
150
- static emit(type: string, ...args: any[]): void;
152
+ static emit(type: string, props?: object): void;
151
153
  }
152
154
 
153
155
  interface CreateUnit {
@@ -173,13 +175,17 @@ interface CreateUnit {
173
175
  (target: HTMLElement | SVGElement | string, Component?: Function | string, props?: Object): Unit;
174
176
  }
175
177
 
176
- declare function OpenAndClose(unit: Unit, { state: initialState }?: {
177
- state?: number;
178
+ declare function OpenAndClose(unit: Unit, { open }?: {
179
+ open?: boolean;
178
180
  }): {
179
181
  toggle(duration?: number, easing?: string): void;
180
182
  open(duration?: number, easing?: string): void;
181
183
  close(duration?: number, easing?: string): void;
182
184
  };
185
+ declare function Accordion(unit: Unit): void;
186
+ declare function Modal(unit: Unit, { background }?: {
187
+ background?: string;
188
+ }): void;
183
189
 
184
190
  type ScreenFit = 'contain' | 'cover';
185
191
  declare function Screen(unit: Unit, { aspect, fit }?: {
@@ -206,6 +212,26 @@ declare function DPad(unit: Unit, { diagonal, stroke, strokeOpacity, strokeWidth
206
212
  fillOpacity?: number;
207
213
  }): void;
208
214
 
215
+ interface GUIPanelOptions {
216
+ name?: string;
217
+ open?: boolean;
218
+ params?: Record<string, any>;
219
+ }
220
+ declare function GUIPanel(unit: Unit, { name, open, params }: GUIPanelOptions): {
221
+ group({ name, open, params }: GUIPanelOptions, inner: Function): Unit;
222
+ button(key: string): Unit;
223
+ select(key: string, { options }?: {
224
+ options?: string[];
225
+ }): Unit;
226
+ range(key: string, options?: {
227
+ min?: number;
228
+ max?: number;
229
+ step?: number;
230
+ }): Unit;
231
+ checkbox(key: string): Unit;
232
+ separator(): void;
233
+ };
234
+
209
235
  type SynthesizerOptions = {
210
236
  oscillator: OscillatorOptions;
211
237
  amp: AmpOptions;
@@ -255,7 +281,7 @@ declare const xnew: CreateUnit & {
255
281
  extend(component: Function, props?: Object): {
256
282
  [key: string]: any;
257
283
  };
258
- context(key: any, value?: any): any;
284
+ context(component: Function): any;
259
285
  promise(promise: Promise<any>): UnitPromise;
260
286
  then(callback: Function): UnitPromise;
261
287
  catch(callback: Function): UnitPromise;
@@ -273,6 +299,9 @@ declare const xnew: CreateUnit & {
273
299
  OpenAndClose: typeof OpenAndClose;
274
300
  AnalogStick: typeof AnalogStick;
275
301
  DPad: typeof DPad;
302
+ GUIPanel: typeof GUIPanel;
303
+ Accordion: typeof Accordion;
304
+ Modal: typeof Modal;
276
305
  };
277
306
  audio: {
278
307
  load(path: string): UnitPromise;
package/dist/xnew.js CHANGED
@@ -223,6 +223,12 @@
223
223
  if (props.type === 'resize') {
224
224
  finalize = this.resize(props);
225
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
+ }
226
232
  else if (props.type === 'wheel') {
227
233
  finalize = this.wheel(props);
228
234
  }
@@ -273,7 +279,7 @@
273
279
  }
274
280
  basic(props) {
275
281
  const execute = (event) => {
276
- props.listener({ event, type: event.type });
282
+ props.listener({ event });
277
283
  };
278
284
  props.element.addEventListener(props.type, execute, props.options);
279
285
  return () => {
@@ -283,7 +289,7 @@
283
289
  resize(props) {
284
290
  const observer = new ResizeObserver((entries) => {
285
291
  for (const entry of entries) {
286
- props.listener({ type: 'resize' });
292
+ props.listener({});
287
293
  break;
288
294
  }
289
295
  });
@@ -292,9 +298,47 @@
292
298
  observer.unobserve(props.element);
293
299
  };
294
300
  }
301
+ change(props) {
302
+ const execute = (event) => {
303
+ let value = null;
304
+ if (event.target.type === 'checkbox') {
305
+ value = event.target.checked;
306
+ }
307
+ else if (event.target.type === 'range' || event.target.type === 'number') {
308
+ value = parseFloat(event.target.value);
309
+ }
310
+ else {
311
+ value = event.target.value;
312
+ }
313
+ props.listener({ event, value });
314
+ };
315
+ props.element.addEventListener(props.type, execute, props.options);
316
+ return () => {
317
+ props.element.removeEventListener(props.type, execute);
318
+ };
319
+ }
320
+ input(props) {
321
+ const execute = (event) => {
322
+ let value = null;
323
+ if (event.target.type === 'checkbox') {
324
+ value = event.target.checked;
325
+ }
326
+ else if (event.target.type === 'range' || event.target.type === 'number') {
327
+ value = parseFloat(event.target.value);
328
+ }
329
+ else {
330
+ value = event.target.value;
331
+ }
332
+ props.listener({ event, value });
333
+ };
334
+ props.element.addEventListener(props.type, execute, props.options);
335
+ return () => {
336
+ props.element.removeEventListener(props.type, execute);
337
+ };
338
+ }
295
339
  click(props) {
296
340
  const execute = (event) => {
297
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
341
+ props.listener({ event, position: pointer(props.element, event).position });
298
342
  };
299
343
  props.element.addEventListener(props.type, execute, props.options);
300
344
  return () => {
@@ -304,7 +348,7 @@
304
348
  click_outside(props) {
305
349
  const execute = (event) => {
306
350
  if (props.element.contains(event.target) === false) {
307
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
351
+ props.listener({ event, position: pointer(props.element, event).position });
308
352
  }
309
353
  };
310
354
  document.addEventListener(props.type.split('.')[0], execute, props.options);
@@ -314,7 +358,7 @@
314
358
  }
315
359
  pointer(props) {
316
360
  const execute = (event) => {
317
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
361
+ props.listener({ event, position: pointer(props.element, event).position });
318
362
  };
319
363
  props.element.addEventListener(props.type, execute, props.options);
320
364
  return () => {
@@ -323,7 +367,7 @@
323
367
  }
324
368
  mouse(props) {
325
369
  const execute = (event) => {
326
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
370
+ props.listener({ event, position: pointer(props.element, event).position });
327
371
  };
328
372
  props.element.addEventListener(props.type, execute, props.options);
329
373
  return () => {
@@ -332,7 +376,7 @@
332
376
  }
333
377
  touch(props) {
334
378
  const execute = (event) => {
335
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
379
+ props.listener({ event, position: pointer(props.element, event).position });
336
380
  };
337
381
  props.element.addEventListener(props.type, execute, props.options);
338
382
  return () => {
@@ -342,7 +386,7 @@
342
386
  pointer_outside(props) {
343
387
  const execute = (event) => {
344
388
  if (props.element.contains(event.target) === false) {
345
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
389
+ props.listener({ event, position: pointer(props.element, event).position });
346
390
  }
347
391
  };
348
392
  document.addEventListener(props.type.split('.')[0], execute, props.options);
@@ -352,7 +396,7 @@
352
396
  }
353
397
  wheel(props) {
354
398
  const execute = (event) => {
355
- props.listener({ event, type: props.type, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } });
399
+ props.listener({ event, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } });
356
400
  };
357
401
  props.element.addEventListener(props.type, execute, props.options);
358
402
  return () => {
@@ -372,7 +416,7 @@
372
416
  const position = pointer(props.element, event).position;
373
417
  const delta = { x: position.x - previous.x, y: position.y - previous.y };
374
418
  if (props.type === 'dragmove') {
375
- props.listener({ event, type: props.type, position, delta });
419
+ props.listener({ event, position, delta });
376
420
  }
377
421
  previous = position;
378
422
  }
@@ -381,7 +425,7 @@
381
425
  if (event.pointerId === id) {
382
426
  const position = pointer(props.element, event).position;
383
427
  if (props.type === 'dragend') {
384
- props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
428
+ props.listener({ event, position, delta: { x: 0, y: 0 } });
385
429
  }
386
430
  remove();
387
431
  }
@@ -390,7 +434,7 @@
390
434
  if (event.pointerId === id) {
391
435
  const position = pointer(props.element, event).position;
392
436
  if (props.type === 'dragend') {
393
- props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
437
+ props.listener({ event, position, delta: { x: 0, y: 0 } });
394
438
  }
395
439
  remove();
396
440
  }
@@ -399,7 +443,7 @@
399
443
  window.addEventListener('pointerup', pointerup);
400
444
  window.addEventListener('pointercancel', pointercancel);
401
445
  if (props.type === 'dragstart') {
402
- props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
446
+ props.listener({ event, position, delta: { x: 0, y: 0 } });
403
447
  }
404
448
  };
405
449
  function remove() {
@@ -428,7 +472,7 @@
428
472
  map.set(event.pointerId, position);
429
473
  isActive = map.size === 2 ? true : false;
430
474
  if (isActive === true && props.type === 'gesturestart') {
431
- props.listener({ event, type: props.type });
475
+ props.listener({ event });
432
476
  }
433
477
  };
434
478
  const dragmove = ({ event, position, delta }) => {
@@ -455,7 +499,7 @@
455
499
  // }
456
500
  // }
457
501
  if (props.type === 'gesturemove') {
458
- props.listener({ event, type: props.type, scale });
502
+ props.listener({ event, scale });
459
503
  }
460
504
  }
461
505
  map.set(event.pointerId, position);
@@ -463,7 +507,7 @@
463
507
  const dragend = ({ event }) => {
464
508
  map.delete(event.pointerId);
465
509
  if (isActive === true && props.type === 'gestureend') {
466
- props.listener({ event, type: props.type, scale: 1.0 });
510
+ props.listener({ event, scale: 1.0 });
467
511
  }
468
512
  isActive = false;
469
513
  };
@@ -487,7 +531,7 @@
487
531
  const execute = (event) => {
488
532
  if (props.type === 'keydown' && event.repeat)
489
533
  return;
490
- props.listener({ event, type: props.type, code: event.code });
534
+ props.listener({ event, code: event.code });
491
535
  };
492
536
  window.addEventListener(props.type, execute, props.options);
493
537
  return () => {
@@ -505,7 +549,7 @@
505
549
  x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
506
550
  y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
507
551
  };
508
- props.listener({ event, type: props.type, code: event.code, vector });
552
+ props.listener({ event, code: event.code, vector });
509
553
  }
510
554
  };
511
555
  const keyup = (event) => {
@@ -515,7 +559,7 @@
515
559
  x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
516
560
  y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
517
561
  };
518
- props.listener({ event, type: props.type, code: event.code, vector });
562
+ props.listener({ event, code: event.code, vector });
519
563
  }
520
564
  };
521
565
  window.addEventListener('keydown', keydown, props.options);
@@ -536,7 +580,7 @@
536
580
  x: (keymap['KeyA'] ? -1 : 0) + (keymap['KeyD'] ? +1 : 0),
537
581
  y: (keymap['KeyW'] ? -1 : 0) + (keymap['KeyS'] ? +1 : 0)
538
582
  };
539
- props.listener({ event, type: props.type, code: event.code, vector });
583
+ props.listener({ event, code: event.code, vector });
540
584
  }
541
585
  };
542
586
  const keyup = (event) => {
@@ -546,7 +590,7 @@
546
590
  x: (keymap['KeyA'] ? -1 : 0) + (keymap['KeyD'] ? +1 : 0),
547
591
  y: (keymap['KeyW'] ? -1 : 0) + (keymap['KeyS'] ? +1 : 0)
548
592
  };
549
- props.listener({ event, type: props.type, code: event.code, vector });
593
+ props.listener({ event, code: event.code, vector });
550
594
  }
551
595
  };
552
596
  window.addEventListener('keydown', keydown, props.options);
@@ -675,7 +719,7 @@
675
719
  baseComponent = (unit) => { };
676
720
  }
677
721
  const baseContext = (_a = parent === null || parent === void 0 ? void 0 : parent._.currentContext) !== null && _a !== void 0 ? _a : { stack: null };
678
- this._ = { parent, target, baseElement, baseContext, baseComponent, props };
722
+ this._ = { parent, target, baseElement, baseContext, baseComponent, props: props !== null && props !== void 0 ? props : {} };
679
723
  parent === null || parent === void 0 ? void 0 : parent._.children.push(this);
680
724
  Unit.initialize(this, null);
681
725
  }
@@ -907,7 +951,9 @@
907
951
  }
908
952
  static on(unit, type, listener, options) {
909
953
  const snapshot = Unit.snapshot(Unit.currentUnit);
910
- const execute = (...args) => Unit.scope(snapshot, listener, ...args);
954
+ const execute = (props) => {
955
+ Unit.scope(snapshot, listener, Object.assign({ type }, props));
956
+ };
911
957
  if (SYSTEM_EVENTS.includes(type)) {
912
958
  unit._.systems[type].push({ listener, execute });
913
959
  }
@@ -936,7 +982,7 @@
936
982
  Unit.type2units.delete(type, unit);
937
983
  }
938
984
  }
939
- static emit(type, ...args) {
985
+ static emit(type, props = {}) {
940
986
  var _a, _b;
941
987
  const current = Unit.currentUnit;
942
988
  if (type[0] === '+') {
@@ -944,12 +990,12 @@
944
990
  var _a;
945
991
  const find = [unit, ...unit._.ancestors].find(u => u._.protected === true);
946
992
  if (find === undefined || current._.ancestors.includes(find) === true || current === find) {
947
- (_a = unit._.listeners.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((item) => item.execute(...args));
993
+ (_a = unit._.listeners.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((item) => item.execute(props));
948
994
  }
949
995
  });
950
996
  }
951
997
  else if (type[0] === '-') {
952
- (_b = current._.listeners.get(type)) === null || _b === void 0 ? void 0 : _b.forEach((item) => item.execute(...args));
998
+ (_b = current._.listeners.get(type)) === null || _b === void 0 ? void 0 : _b.forEach((item) => item.execute(props));
953
999
  }
954
1000
  }
955
1001
  }
@@ -1227,8 +1273,8 @@
1227
1273
  },
1228
1274
  });
1229
1275
 
1230
- function OpenAndClose(unit, { state: initialState = 0.0 } = {}) {
1231
- let state = Math.max(0.0, Math.min(1.0, initialState));
1276
+ function OpenAndClose(unit, { open = false } = {}) {
1277
+ let state = open ? 1.0 : 0.0;
1232
1278
  let direction = state === 1.0 ? +1 : (state === 0.0 ? -1 : null);
1233
1279
  let timer = xnew$1.timeout(() => xnew$1.emit('-transition', { state }));
1234
1280
  return {
@@ -1248,10 +1294,10 @@
1248
1294
  timer = xnew$1.transition((x) => {
1249
1295
  const y = x < 1.0 ? (1 - x) * d : 0.0;
1250
1296
  state = 1.0 - y;
1251
- xnew$1.emit('-transition', { state, type: '-transition' });
1297
+ xnew$1.emit('-transition', { state });
1252
1298
  }, duration * d, easing)
1253
1299
  .timeout(() => {
1254
- xnew$1.emit('-opened', { state, type: '-opened' });
1300
+ xnew$1.emit('-opened', { state });
1255
1301
  });
1256
1302
  }
1257
1303
  },
@@ -1263,15 +1309,37 @@
1263
1309
  timer = xnew$1.transition((x) => {
1264
1310
  const y = x < 1.0 ? (1 - x) * d : 0.0;
1265
1311
  state = y;
1266
- xnew$1.emit('-transition', { state, type: '-transition' });
1312
+ xnew$1.emit('-transition', { state });
1267
1313
  }, duration * d, easing)
1268
1314
  .timeout(() => {
1269
- xnew$1.emit('-closed', { state, type: '-closed' });
1315
+ xnew$1.emit('-closed', { state });
1270
1316
  });
1271
1317
  }
1272
1318
  },
1273
1319
  };
1274
1320
  }
1321
+ function Accordion(unit) {
1322
+ const system = xnew$1.context(OpenAndClose);
1323
+ const outer = xnew$1.nest('<div style="overflow: hidden;">');
1324
+ const inner = xnew$1.nest('<div style="display: flex; flex-direction: column; box-sizing: border-box;">');
1325
+ system.on('-transition', ({ state }) => {
1326
+ outer.style.height = state < 1.0 ? inner.offsetHeight * state + 'px' : 'auto';
1327
+ outer.style.opacity = state.toString();
1328
+ });
1329
+ }
1330
+ function Modal(unit, { background = 'rgba(0, 0, 0, 0.1)' } = {}) {
1331
+ const system = xnew$1.context(OpenAndClose);
1332
+ system.on('-closed', () => unit.finalize());
1333
+ xnew$1.nest('<div style="position: fixed; inset: 0; z-index: 1000;">');
1334
+ unit.on('click', ({ event }) => system.close());
1335
+ const outer = xnew$1.nest(`<div style="width: 100%; height: 100%; opacity: 0;"">`);
1336
+ xnew$1.nest('<div style="position: absolute; inset: 0; margin: auto; width: max-content; height: max-content;">');
1337
+ unit.on('click', ({ event }) => event.stopPropagation());
1338
+ outer.style.background = background;
1339
+ system.on('-transition', ({ state }) => {
1340
+ outer.style.opacity = state.toString();
1341
+ });
1342
+ }
1275
1343
 
1276
1344
  function Screen(unit, { aspect, fit = 'contain' } = {}) {
1277
1345
  xnew$1.nest('<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size; overflow: hidden;">');
@@ -1297,14 +1365,8 @@
1297
1365
  ">`);
1298
1366
  }
1299
1367
  function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1300
- const outer = xnew$1.nest(`<div style="position: relative; width: 100%; height: 100%;">`);
1301
- let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1302
- 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;">`);
1303
- xnew$1(outer).on('resize', () => {
1304
- newsize = Math.min(outer.clientWidth, outer.clientHeight);
1305
- inner.style.width = `${newsize}px`;
1306
- inner.style.height = `${newsize}px`;
1307
- });
1368
+ xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1369
+ xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1308
1370
  xnew$1((unit) => {
1309
1371
  xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1310
1372
  xnew$1('<polygon points="32 7 27 13 37 13">');
@@ -1317,34 +1379,30 @@
1317
1379
  xnew$1('<circle cx="32" cy="32" r="14">');
1318
1380
  });
1319
1381
  unit.on('dragstart dragmove', ({ type, position }) => {
1320
- const x = position.x - newsize / 2;
1321
- const y = position.y - newsize / 2;
1322
- const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (newsize / 4));
1382
+ const size = unit.element.clientWidth;
1383
+ const x = position.x - size / 2;
1384
+ const y = position.y - size / 2;
1385
+ const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (size / 4));
1323
1386
  const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1324
1387
  const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1325
1388
  target.element.style.filter = 'brightness(80%)';
1326
- target.element.style.left = `${vector.x * newsize / 4}px`;
1327
- target.element.style.top = `${vector.y * newsize / 4}px`;
1389
+ target.element.style.left = `${vector.x * size / 4}px`;
1390
+ target.element.style.top = `${vector.y * size / 4}px`;
1328
1391
  const nexttype = { dragstart: '-down', dragmove: '-move' }[type];
1329
1392
  xnew$1.emit(nexttype, { type: nexttype, vector });
1330
1393
  });
1331
1394
  unit.on('dragend', () => {
1395
+ const size = unit.element.clientWidth;
1332
1396
  const vector = { x: 0, y: 0 };
1333
1397
  target.element.style.filter = '';
1334
- target.element.style.left = `${vector.x * newsize / 4}px`;
1335
- target.element.style.top = `${vector.y * newsize / 4}px`;
1398
+ target.element.style.left = `${vector.x * size / 4}px`;
1399
+ target.element.style.top = `${vector.y * size / 4}px`;
1336
1400
  xnew$1.emit('-up', { type: '-up', vector });
1337
1401
  });
1338
1402
  }
1339
1403
  function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1340
- const outer = xnew$1.nest(`<div style="position: relative; width: 100%; height: 100%;">`);
1341
- let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1342
- 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;">`);
1343
- xnew$1(outer).on('resize', () => {
1344
- newsize = Math.min(outer.clientWidth, outer.clientHeight);
1345
- inner.style.width = `${newsize}px`;
1346
- inner.style.height = `${newsize}px`;
1347
- });
1404
+ xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1405
+ xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1348
1406
  const polygons = [
1349
1407
  '<polygon points="32 32 23 23 23 4 24 3 40 3 41 4 41 23">',
1350
1408
  '<polygon points="32 32 23 41 23 60 24 61 40 61 41 60 41 41">',
@@ -1369,10 +1427,11 @@
1369
1427
  xnew$1('<polygon points="57 32 51 27 51 37">');
1370
1428
  });
1371
1429
  unit.on('dragstart dragmove', ({ type, position }) => {
1372
- const x = position.x - newsize / 2;
1373
- const y = position.y - newsize / 2;
1430
+ const size = unit.element.clientWidth;
1431
+ const x = position.x - size / 2;
1432
+ const y = position.y - size / 2;
1374
1433
  const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1375
- const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (newsize / 4));
1434
+ const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (size / 4));
1376
1435
  const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1377
1436
  if (diagonal === true) {
1378
1437
  vector.x = Math.abs(vector.x) > 0.5 ? Math.sign(vector.x) : 0;
@@ -1391,7 +1450,7 @@
1391
1450
  targets[2].element.style.filter = (vector.x < 0) ? 'brightness(80%)' : '';
1392
1451
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(80%)' : '';
1393
1452
  const nexttype = { dragstart: '-down', dragmove: '-move' }[type];
1394
- xnew$1.emit(nexttype, { type: nexttype, vector });
1453
+ xnew$1.emit(nexttype, { vector });
1395
1454
  });
1396
1455
  unit.on('dragend', () => {
1397
1456
  const vector = { x: 0, y: 0 };
@@ -1399,10 +1458,125 @@
1399
1458
  targets[1].element.style.filter = '';
1400
1459
  targets[2].element.style.filter = '';
1401
1460
  targets[3].element.style.filter = '';
1402
- xnew$1.emit('-up', { type: '-up', vector });
1461
+ xnew$1.emit('-up', { vector });
1403
1462
  });
1404
1463
  }
1405
1464
 
1465
+ function GUIPanel(unit, { name, open = false, params }) {
1466
+ const object = params !== null && params !== void 0 ? params : {};
1467
+ xnew$1.extend(Group, { name, open });
1468
+ return {
1469
+ group({ name, open, params }, inner) {
1470
+ const group = xnew$1((unit) => {
1471
+ xnew$1.extend(GUIPanel, { name, open, params: params !== null && params !== void 0 ? params : object });
1472
+ inner(unit);
1473
+ });
1474
+ group.on('-eventcapture', ({ event, key, value }) => {
1475
+ xnew$1.emit('-eventcapture', { event, key, value });
1476
+ });
1477
+ return group;
1478
+ },
1479
+ button(key) {
1480
+ const button = xnew$1(Button, { key });
1481
+ button.on('click', ({ event }) => {
1482
+ xnew$1.emit('-eventcapture', { event, key });
1483
+ });
1484
+ return button;
1485
+ },
1486
+ select(key, { options = [] } = {}) {
1487
+ var _a, _b;
1488
+ object[key] = (_b = (_a = object[key]) !== null && _a !== void 0 ? _a : options[0]) !== null && _b !== void 0 ? _b : '';
1489
+ const select = xnew$1(Select, { key, value: object[key], options });
1490
+ select.on('input', ({ event, value }) => {
1491
+ xnew$1.emit('-eventcapture', { event, key, value });
1492
+ });
1493
+ return select;
1494
+ },
1495
+ range(key, options = {}) {
1496
+ var _a;
1497
+ object[key] = (_a = object[key]) !== null && _a !== void 0 ? _a : 0;
1498
+ const number = xnew$1(Range, Object.assign({ key, value: object[key] }, options));
1499
+ number.on('input', ({ event, value }) => {
1500
+ object[key] = value;
1501
+ xnew$1.emit('-eventcapture', { event, key, value });
1502
+ });
1503
+ return number;
1504
+ },
1505
+ checkbox(key) {
1506
+ var _a;
1507
+ object[key] = (_a = object[key]) !== null && _a !== void 0 ? _a : false;
1508
+ const checkbox = xnew$1(Checkbox, { key, value: object[key] });
1509
+ checkbox.on('input', ({ event, value }) => {
1510
+ object[key] = value;
1511
+ xnew$1.emit('-eventcapture', { event, key, value });
1512
+ });
1513
+ return checkbox;
1514
+ },
1515
+ separator() {
1516
+ xnew$1(Separator);
1517
+ }
1518
+ };
1519
+ }
1520
+ function Group(group, { name, open = false }) {
1521
+ xnew$1.extend(OpenAndClose, { open });
1522
+ if (name) {
1523
+ xnew$1('<div style="display: flex; align-items: center; cursor: pointer;">', (unit) => {
1524
+ unit.on('click', () => group.toggle());
1525
+ xnew$1('<svg viewBox="0 0 12 12" style="width: 1rem; height: 1rem; margin-right: 0.25rem;" fill="none" stroke="currentColor">', (unit) => {
1526
+ xnew$1('<path d="M6 2 10 6 6 10" />');
1527
+ group.on('-transition', ({ state }) => unit.element.style.transform = `rotate(${state * 90}deg)`);
1528
+ });
1529
+ xnew$1('<div>', name);
1530
+ });
1531
+ }
1532
+ xnew$1.extend(Accordion);
1533
+ }
1534
+ function Button(unit, { key = '' }) {
1535
+ xnew$1.nest('<button style="margin: 0.125rem; padding: 0.125rem; border: 1px solid; border-radius: 0.25rem; cursor: pointer;">');
1536
+ unit.element.textContent = key;
1537
+ unit.on('pointerover', () => {
1538
+ unit.element.style.background = 'rgba(0, 0, 128, 0.1)';
1539
+ unit.element.style.borderColor = 'blue';
1540
+ });
1541
+ unit.on('pointerout', () => {
1542
+ unit.element.style.background = '';
1543
+ unit.element.style.borderColor = '';
1544
+ });
1545
+ unit.on('pointerdown', () => {
1546
+ unit.element.style.filter = 'brightness(0.5)';
1547
+ });
1548
+ unit.on('pointerup', () => {
1549
+ unit.element.style.filter = '';
1550
+ });
1551
+ }
1552
+ function Separator(unit) {
1553
+ xnew$1.nest('<div style="margin: 0.5rem 0; border-top: 1px solid;">');
1554
+ }
1555
+ function Range(unit, { key = '', value, min = 0, max = 100, step = 1 }) {
1556
+ xnew$1.nest(`<div style="margin: 0.125rem;">`);
1557
+ const status = xnew$1('<div style="display: flex; justify-content: space-between;">', (unit) => {
1558
+ xnew$1('<div style="flex: 1;">', key);
1559
+ xnew$1('<div key="status" style="flex: none;">', value);
1560
+ });
1561
+ xnew$1.nest(`<input type="range" name="${key}" min="${min}" max="${max}" step="${step}" value="${value}" style="width: 100%; cursor: pointer;">`);
1562
+ unit.on('input', ({ event }) => {
1563
+ status.element.querySelector('[key="status"]').textContent = event.target.value;
1564
+ });
1565
+ }
1566
+ function Checkbox(unit, { key = '', value } = {}) {
1567
+ xnew$1.nest(`<label style="margin: 0.125rem; display: flex; align-items: center; cursor: pointer;">`);
1568
+ xnew$1('<div style="flex: 1;">', key);
1569
+ xnew$1.nest(`<input type="checkbox" name="${key}" ${value ? 'checked' : ''} style="margin-right: 0.25rem;">`);
1570
+ }
1571
+ function Select(unit, { key = '', value, options = [] } = {}) {
1572
+ xnew$1.nest(`<div style="margin: 0.125rem; display: flex; align-items: center;">`);
1573
+ xnew$1('<div style="flex: 1;">', key);
1574
+ xnew$1.nest(`<select name="${key}" style="padding: 0.125rem; border: 1px solid; border-radius: 0.25rem; cursor: pointer;">`);
1575
+ for (const option of options) {
1576
+ xnew$1(`<option value="${option}" ${option === value ? 'selected' : ''}>`, option);
1577
+ }
1578
+ }
1579
+
1406
1580
  const context = new window.AudioContext();
1407
1581
  const master = context.createGain();
1408
1582
  //----------------------------------------------------------------------------------------------------
@@ -1632,6 +1806,9 @@
1632
1806
  OpenAndClose,
1633
1807
  AnalogStick,
1634
1808
  DPad,
1809
+ GUIPanel,
1810
+ Accordion,
1811
+ Modal,
1635
1812
  };
1636
1813
  const audio = {
1637
1814
  load(path) {
package/dist/xnew.mjs CHANGED
@@ -217,6 +217,12 @@ class Eventor {
217
217
  if (props.type === 'resize') {
218
218
  finalize = this.resize(props);
219
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
+ }
220
226
  else if (props.type === 'wheel') {
221
227
  finalize = this.wheel(props);
222
228
  }
@@ -267,7 +273,7 @@ class Eventor {
267
273
  }
268
274
  basic(props) {
269
275
  const execute = (event) => {
270
- props.listener({ event, type: event.type });
276
+ props.listener({ event });
271
277
  };
272
278
  props.element.addEventListener(props.type, execute, props.options);
273
279
  return () => {
@@ -277,7 +283,7 @@ class Eventor {
277
283
  resize(props) {
278
284
  const observer = new ResizeObserver((entries) => {
279
285
  for (const entry of entries) {
280
- props.listener({ type: 'resize' });
286
+ props.listener({});
281
287
  break;
282
288
  }
283
289
  });
@@ -286,9 +292,47 @@ class Eventor {
286
292
  observer.unobserve(props.element);
287
293
  };
288
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
+ }
289
333
  click(props) {
290
334
  const execute = (event) => {
291
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
335
+ props.listener({ event, position: pointer(props.element, event).position });
292
336
  };
293
337
  props.element.addEventListener(props.type, execute, props.options);
294
338
  return () => {
@@ -298,7 +342,7 @@ class Eventor {
298
342
  click_outside(props) {
299
343
  const execute = (event) => {
300
344
  if (props.element.contains(event.target) === false) {
301
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
345
+ props.listener({ event, position: pointer(props.element, event).position });
302
346
  }
303
347
  };
304
348
  document.addEventListener(props.type.split('.')[0], execute, props.options);
@@ -308,7 +352,7 @@ class Eventor {
308
352
  }
309
353
  pointer(props) {
310
354
  const execute = (event) => {
311
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
355
+ props.listener({ event, position: pointer(props.element, event).position });
312
356
  };
313
357
  props.element.addEventListener(props.type, execute, props.options);
314
358
  return () => {
@@ -317,7 +361,7 @@ class Eventor {
317
361
  }
318
362
  mouse(props) {
319
363
  const execute = (event) => {
320
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
364
+ props.listener({ event, position: pointer(props.element, event).position });
321
365
  };
322
366
  props.element.addEventListener(props.type, execute, props.options);
323
367
  return () => {
@@ -326,7 +370,7 @@ class Eventor {
326
370
  }
327
371
  touch(props) {
328
372
  const execute = (event) => {
329
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
373
+ props.listener({ event, position: pointer(props.element, event).position });
330
374
  };
331
375
  props.element.addEventListener(props.type, execute, props.options);
332
376
  return () => {
@@ -336,7 +380,7 @@ class Eventor {
336
380
  pointer_outside(props) {
337
381
  const execute = (event) => {
338
382
  if (props.element.contains(event.target) === false) {
339
- props.listener({ event, type: props.type, position: pointer(props.element, event).position });
383
+ props.listener({ event, position: pointer(props.element, event).position });
340
384
  }
341
385
  };
342
386
  document.addEventListener(props.type.split('.')[0], execute, props.options);
@@ -346,7 +390,7 @@ class Eventor {
346
390
  }
347
391
  wheel(props) {
348
392
  const execute = (event) => {
349
- props.listener({ event, type: props.type, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } });
393
+ props.listener({ event, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } });
350
394
  };
351
395
  props.element.addEventListener(props.type, execute, props.options);
352
396
  return () => {
@@ -366,7 +410,7 @@ class Eventor {
366
410
  const position = pointer(props.element, event).position;
367
411
  const delta = { x: position.x - previous.x, y: position.y - previous.y };
368
412
  if (props.type === 'dragmove') {
369
- props.listener({ event, type: props.type, position, delta });
413
+ props.listener({ event, position, delta });
370
414
  }
371
415
  previous = position;
372
416
  }
@@ -375,7 +419,7 @@ class Eventor {
375
419
  if (event.pointerId === id) {
376
420
  const position = pointer(props.element, event).position;
377
421
  if (props.type === 'dragend') {
378
- props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
422
+ props.listener({ event, position, delta: { x: 0, y: 0 } });
379
423
  }
380
424
  remove();
381
425
  }
@@ -384,7 +428,7 @@ class Eventor {
384
428
  if (event.pointerId === id) {
385
429
  const position = pointer(props.element, event).position;
386
430
  if (props.type === 'dragend') {
387
- props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
431
+ props.listener({ event, position, delta: { x: 0, y: 0 } });
388
432
  }
389
433
  remove();
390
434
  }
@@ -393,7 +437,7 @@ class Eventor {
393
437
  window.addEventListener('pointerup', pointerup);
394
438
  window.addEventListener('pointercancel', pointercancel);
395
439
  if (props.type === 'dragstart') {
396
- props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
440
+ props.listener({ event, position, delta: { x: 0, y: 0 } });
397
441
  }
398
442
  };
399
443
  function remove() {
@@ -422,7 +466,7 @@ class Eventor {
422
466
  map.set(event.pointerId, position);
423
467
  isActive = map.size === 2 ? true : false;
424
468
  if (isActive === true && props.type === 'gesturestart') {
425
- props.listener({ event, type: props.type });
469
+ props.listener({ event });
426
470
  }
427
471
  };
428
472
  const dragmove = ({ event, position, delta }) => {
@@ -449,7 +493,7 @@ class Eventor {
449
493
  // }
450
494
  // }
451
495
  if (props.type === 'gesturemove') {
452
- props.listener({ event, type: props.type, scale });
496
+ props.listener({ event, scale });
453
497
  }
454
498
  }
455
499
  map.set(event.pointerId, position);
@@ -457,7 +501,7 @@ class Eventor {
457
501
  const dragend = ({ event }) => {
458
502
  map.delete(event.pointerId);
459
503
  if (isActive === true && props.type === 'gestureend') {
460
- props.listener({ event, type: props.type, scale: 1.0 });
504
+ props.listener({ event, scale: 1.0 });
461
505
  }
462
506
  isActive = false;
463
507
  };
@@ -481,7 +525,7 @@ class Eventor {
481
525
  const execute = (event) => {
482
526
  if (props.type === 'keydown' && event.repeat)
483
527
  return;
484
- props.listener({ event, type: props.type, code: event.code });
528
+ props.listener({ event, code: event.code });
485
529
  };
486
530
  window.addEventListener(props.type, execute, props.options);
487
531
  return () => {
@@ -499,7 +543,7 @@ class Eventor {
499
543
  x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
500
544
  y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
501
545
  };
502
- props.listener({ event, type: props.type, code: event.code, vector });
546
+ props.listener({ event, code: event.code, vector });
503
547
  }
504
548
  };
505
549
  const keyup = (event) => {
@@ -509,7 +553,7 @@ class Eventor {
509
553
  x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
510
554
  y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
511
555
  };
512
- props.listener({ event, type: props.type, code: event.code, vector });
556
+ props.listener({ event, code: event.code, vector });
513
557
  }
514
558
  };
515
559
  window.addEventListener('keydown', keydown, props.options);
@@ -530,7 +574,7 @@ class Eventor {
530
574
  x: (keymap['KeyA'] ? -1 : 0) + (keymap['KeyD'] ? +1 : 0),
531
575
  y: (keymap['KeyW'] ? -1 : 0) + (keymap['KeyS'] ? +1 : 0)
532
576
  };
533
- props.listener({ event, type: props.type, code: event.code, vector });
577
+ props.listener({ event, code: event.code, vector });
534
578
  }
535
579
  };
536
580
  const keyup = (event) => {
@@ -540,7 +584,7 @@ class Eventor {
540
584
  x: (keymap['KeyA'] ? -1 : 0) + (keymap['KeyD'] ? +1 : 0),
541
585
  y: (keymap['KeyW'] ? -1 : 0) + (keymap['KeyS'] ? +1 : 0)
542
586
  };
543
- props.listener({ event, type: props.type, code: event.code, vector });
587
+ props.listener({ event, code: event.code, vector });
544
588
  }
545
589
  };
546
590
  window.addEventListener('keydown', keydown, props.options);
@@ -669,7 +713,7 @@ class Unit {
669
713
  baseComponent = (unit) => { };
670
714
  }
671
715
  const baseContext = (_a = parent === null || parent === void 0 ? void 0 : parent._.currentContext) !== null && _a !== void 0 ? _a : { stack: null };
672
- this._ = { parent, target, baseElement, baseContext, baseComponent, props };
716
+ this._ = { parent, target, baseElement, baseContext, baseComponent, props: props !== null && props !== void 0 ? props : {} };
673
717
  parent === null || parent === void 0 ? void 0 : parent._.children.push(this);
674
718
  Unit.initialize(this, null);
675
719
  }
@@ -901,7 +945,9 @@ class Unit {
901
945
  }
902
946
  static on(unit, type, listener, options) {
903
947
  const snapshot = Unit.snapshot(Unit.currentUnit);
904
- const execute = (...args) => Unit.scope(snapshot, listener, ...args);
948
+ const execute = (props) => {
949
+ Unit.scope(snapshot, listener, Object.assign({ type }, props));
950
+ };
905
951
  if (SYSTEM_EVENTS.includes(type)) {
906
952
  unit._.systems[type].push({ listener, execute });
907
953
  }
@@ -930,7 +976,7 @@ class Unit {
930
976
  Unit.type2units.delete(type, unit);
931
977
  }
932
978
  }
933
- static emit(type, ...args) {
979
+ static emit(type, props = {}) {
934
980
  var _a, _b;
935
981
  const current = Unit.currentUnit;
936
982
  if (type[0] === '+') {
@@ -938,12 +984,12 @@ class Unit {
938
984
  var _a;
939
985
  const find = [unit, ...unit._.ancestors].find(u => u._.protected === true);
940
986
  if (find === undefined || current._.ancestors.includes(find) === true || current === find) {
941
- (_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));
942
988
  }
943
989
  });
944
990
  }
945
991
  else if (type[0] === '-') {
946
- (_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));
947
993
  }
948
994
  }
949
995
  }
@@ -1221,8 +1267,8 @@ const xnew$1 = Object.assign(function (...args) {
1221
1267
  },
1222
1268
  });
1223
1269
 
1224
- function OpenAndClose(unit, { state: initialState = 0.0 } = {}) {
1225
- 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;
1226
1272
  let direction = state === 1.0 ? +1 : (state === 0.0 ? -1 : null);
1227
1273
  let timer = xnew$1.timeout(() => xnew$1.emit('-transition', { state }));
1228
1274
  return {
@@ -1242,10 +1288,10 @@ function OpenAndClose(unit, { state: initialState = 0.0 } = {}) {
1242
1288
  timer = xnew$1.transition((x) => {
1243
1289
  const y = x < 1.0 ? (1 - x) * d : 0.0;
1244
1290
  state = 1.0 - y;
1245
- xnew$1.emit('-transition', { state, type: '-transition' });
1291
+ xnew$1.emit('-transition', { state });
1246
1292
  }, duration * d, easing)
1247
1293
  .timeout(() => {
1248
- xnew$1.emit('-opened', { state, type: '-opened' });
1294
+ xnew$1.emit('-opened', { state });
1249
1295
  });
1250
1296
  }
1251
1297
  },
@@ -1257,15 +1303,37 @@ function OpenAndClose(unit, { state: initialState = 0.0 } = {}) {
1257
1303
  timer = xnew$1.transition((x) => {
1258
1304
  const y = x < 1.0 ? (1 - x) * d : 0.0;
1259
1305
  state = y;
1260
- xnew$1.emit('-transition', { state, type: '-transition' });
1306
+ xnew$1.emit('-transition', { state });
1261
1307
  }, duration * d, easing)
1262
1308
  .timeout(() => {
1263
- xnew$1.emit('-closed', { state, type: '-closed' });
1309
+ xnew$1.emit('-closed', { state });
1264
1310
  });
1265
1311
  }
1266
1312
  },
1267
1313
  };
1268
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
+ }
1269
1337
 
1270
1338
  function Screen(unit, { aspect, fit = 'contain' } = {}) {
1271
1339
  xnew$1.nest('<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size; overflow: hidden;">');
@@ -1291,14 +1359,8 @@ function SVGTemplate(self, { stroke = 'currentColor', strokeOpacity = 0.8, strok
1291
1359
  ">`);
1292
1360
  }
1293
1361
  function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1294
- const outer = xnew$1.nest(`<div style="position: relative; width: 100%; height: 100%;">`);
1295
- let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1296
- 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;">`);
1297
- xnew$1(outer).on('resize', () => {
1298
- newsize = Math.min(outer.clientWidth, outer.clientHeight);
1299
- inner.style.width = `${newsize}px`;
1300
- inner.style.height = `${newsize}px`;
1301
- });
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;">`);
1302
1364
  xnew$1((unit) => {
1303
1365
  xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1304
1366
  xnew$1('<polygon points="32 7 27 13 37 13">');
@@ -1311,34 +1373,30 @@ function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strok
1311
1373
  xnew$1('<circle cx="32" cy="32" r="14">');
1312
1374
  });
1313
1375
  unit.on('dragstart dragmove', ({ type, position }) => {
1314
- const x = position.x - newsize / 2;
1315
- const y = position.y - newsize / 2;
1316
- 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));
1317
1380
  const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1318
1381
  const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1319
1382
  target.element.style.filter = 'brightness(80%)';
1320
- target.element.style.left = `${vector.x * newsize / 4}px`;
1321
- 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`;
1322
1385
  const nexttype = { dragstart: '-down', dragmove: '-move' }[type];
1323
1386
  xnew$1.emit(nexttype, { type: nexttype, vector });
1324
1387
  });
1325
1388
  unit.on('dragend', () => {
1389
+ const size = unit.element.clientWidth;
1326
1390
  const vector = { x: 0, y: 0 };
1327
1391
  target.element.style.filter = '';
1328
- target.element.style.left = `${vector.x * newsize / 4}px`;
1329
- 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`;
1330
1394
  xnew$1.emit('-up', { type: '-up', vector });
1331
1395
  });
1332
1396
  }
1333
1397
  function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1334
- const outer = xnew$1.nest(`<div style="position: relative; width: 100%; height: 100%;">`);
1335
- let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1336
- 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;">`);
1337
- xnew$1(outer).on('resize', () => {
1338
- newsize = Math.min(outer.clientWidth, outer.clientHeight);
1339
- inner.style.width = `${newsize}px`;
1340
- inner.style.height = `${newsize}px`;
1341
- });
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;">`);
1342
1400
  const polygons = [
1343
1401
  '<polygon points="32 32 23 23 23 4 24 3 40 3 41 4 41 23">',
1344
1402
  '<polygon points="32 32 23 41 23 60 24 61 40 61 41 60 41 41">',
@@ -1363,10 +1421,11 @@ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity =
1363
1421
  xnew$1('<polygon points="57 32 51 27 51 37">');
1364
1422
  });
1365
1423
  unit.on('dragstart dragmove', ({ type, position }) => {
1366
- const x = position.x - newsize / 2;
1367
- 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;
1368
1427
  const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1369
- 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));
1370
1429
  const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1371
1430
  if (diagonal === true) {
1372
1431
  vector.x = Math.abs(vector.x) > 0.5 ? Math.sign(vector.x) : 0;
@@ -1385,7 +1444,7 @@ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity =
1385
1444
  targets[2].element.style.filter = (vector.x < 0) ? 'brightness(80%)' : '';
1386
1445
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(80%)' : '';
1387
1446
  const nexttype = { dragstart: '-down', dragmove: '-move' }[type];
1388
- xnew$1.emit(nexttype, { type: nexttype, vector });
1447
+ xnew$1.emit(nexttype, { vector });
1389
1448
  });
1390
1449
  unit.on('dragend', () => {
1391
1450
  const vector = { x: 0, y: 0 };
@@ -1393,10 +1452,125 @@ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity =
1393
1452
  targets[1].element.style.filter = '';
1394
1453
  targets[2].element.style.filter = '';
1395
1454
  targets[3].element.style.filter = '';
1396
- xnew$1.emit('-up', { type: '-up', vector });
1455
+ xnew$1.emit('-up', { vector });
1397
1456
  });
1398
1457
  }
1399
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
+
1400
1574
  const context = new window.AudioContext();
1401
1575
  const master = context.createGain();
1402
1576
  //----------------------------------------------------------------------------------------------------
@@ -1626,6 +1800,9 @@ const basics = {
1626
1800
  OpenAndClose,
1627
1801
  AnalogStick,
1628
1802
  DPad,
1803
+ GUIPanel,
1804
+ Accordion,
1805
+ Modal,
1629
1806
  };
1630
1807
  const audio = {
1631
1808
  load(path) {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "keywords": [
5
5
  "Component-Oriented Programming"
6
6
  ],
7
- "version": "0.6.0",
7
+ "version": "0.6.1",
8
8
  "main": "dist/xnew.js",
9
9
  "module": "dist/xnew.mjs",
10
10
  "types": "dist/xnew.d.ts",