@mulsense/xnew 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/xnew.js CHANGED
@@ -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,51 +1273,59 @@
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));
1232
- let direction = state === 1.0 ? +1 : (state === 0.0 ? -1 : null);
1276
+ function OpenAndClose(unit, { open = false }) {
1277
+ let state = open ? 1.0 : 0.0;
1278
+ let sign = open ? +1 : -1;
1233
1279
  let timer = xnew$1.timeout(() => xnew$1.emit('-transition', { state }));
1234
1280
  return {
1235
1281
  toggle(duration = 200, easing = 'ease') {
1236
- if (direction === null || direction < 0) {
1237
- unit.open(duration, easing);
1238
- }
1239
- else {
1240
- unit.close(duration, easing);
1241
- }
1282
+ sign < 0 ? unit.open(duration, easing) : unit.close(duration, easing);
1242
1283
  },
1243
1284
  open(duration = 200, easing = 'ease') {
1244
- if (direction === null || direction < 0) {
1245
- direction = +1;
1246
- const d = 1 - state;
1247
- timer === null || timer === void 0 ? void 0 : timer.clear();
1248
- timer = xnew$1.transition((x) => {
1249
- const y = x < 1.0 ? (1 - x) * d : 0.0;
1250
- state = 1.0 - y;
1251
- xnew$1.emit('-transition', { state, type: '-transition' });
1252
- }, duration * d, easing)
1253
- .timeout(() => {
1254
- xnew$1.emit('-opened', { state, type: '-opened' });
1255
- });
1256
- }
1285
+ sign = +1;
1286
+ const d = 1 - state;
1287
+ timer === null || timer === void 0 ? void 0 : timer.clear();
1288
+ timer = xnew$1.transition((x) => {
1289
+ state = 1.0 - (x < 1.0 ? (1 - x) * d : 0.0);
1290
+ xnew$1.emit('-transition', { state });
1291
+ }, duration * d, easing)
1292
+ .timeout(() => xnew$1.emit('-opened', { state }));
1257
1293
  },
1258
1294
  close(duration = 200, easing = 'ease') {
1259
- if (direction === null || direction > 0) {
1260
- direction = -1;
1261
- const d = state;
1262
- timer === null || timer === void 0 ? void 0 : timer.clear();
1263
- timer = xnew$1.transition((x) => {
1264
- const y = x < 1.0 ? (1 - x) * d : 0.0;
1265
- state = y;
1266
- xnew$1.emit('-transition', { state, type: '-transition' });
1267
- }, duration * d, easing)
1268
- .timeout(() => {
1269
- xnew$1.emit('-closed', { state, type: '-closed' });
1270
- });
1271
- }
1295
+ sign = -1;
1296
+ const d = state;
1297
+ timer === null || timer === void 0 ? void 0 : timer.clear();
1298
+ timer = xnew$1.transition((x) => {
1299
+ state = x < 1.0 ? (1 - x) * d : 0.0;
1300
+ xnew$1.emit('-transition', { state });
1301
+ }, duration * d, easing)
1302
+ .timeout(() => xnew$1.emit('-closed', { state }));
1272
1303
  },
1273
1304
  };
1274
1305
  }
1306
+ function Accordion(unit) {
1307
+ const system = xnew$1.context(OpenAndClose);
1308
+ const outer = xnew$1.nest('<div style="overflow: hidden;">');
1309
+ const inner = xnew$1.nest('<div style="display: flex; flex-direction: column; box-sizing: border-box;">');
1310
+ system.on('-transition', ({ state }) => {
1311
+ outer.style.height = state < 1.0 ? inner.offsetHeight * state + 'px' : 'auto';
1312
+ outer.style.opacity = state.toString();
1313
+ });
1314
+ }
1315
+ function Modal(unit) {
1316
+ const system = xnew$1.context(OpenAndClose);
1317
+ system.open();
1318
+ system.on('-closed', () => unit.finalize());
1319
+ xnew$1.nest('<div style="position: absolute; inset: 0; z-index: 1000; opacity: 0;">');
1320
+ unit.on('click', ({ event }) => {
1321
+ if (event.target === unit.element) {
1322
+ system.close();
1323
+ }
1324
+ });
1325
+ system.on('-transition', ({ state }) => {
1326
+ unit.element.style.opacity = state.toString();
1327
+ });
1328
+ }
1275
1329
 
1276
1330
  function Screen(unit, { aspect, fit = 'contain' } = {}) {
1277
1331
  xnew$1.nest('<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size; overflow: hidden;">');
@@ -1297,14 +1351,8 @@
1297
1351
  ">`);
1298
1352
  }
1299
1353
  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
- });
1354
+ xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1355
+ xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1308
1356
  xnew$1((unit) => {
1309
1357
  xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1310
1358
  xnew$1('<polygon points="32 7 27 13 37 13">');
@@ -1317,34 +1365,30 @@
1317
1365
  xnew$1('<circle cx="32" cy="32" r="14">');
1318
1366
  });
1319
1367
  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));
1368
+ const size = unit.element.clientWidth;
1369
+ const x = position.x - size / 2;
1370
+ const y = position.y - size / 2;
1371
+ const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (size / 4));
1323
1372
  const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1324
1373
  const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1325
1374
  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`;
1375
+ target.element.style.left = `${vector.x * size / 4}px`;
1376
+ target.element.style.top = `${vector.y * size / 4}px`;
1328
1377
  const nexttype = { dragstart: '-down', dragmove: '-move' }[type];
1329
1378
  xnew$1.emit(nexttype, { type: nexttype, vector });
1330
1379
  });
1331
1380
  unit.on('dragend', () => {
1381
+ const size = unit.element.clientWidth;
1332
1382
  const vector = { x: 0, y: 0 };
1333
1383
  target.element.style.filter = '';
1334
- target.element.style.left = `${vector.x * newsize / 4}px`;
1335
- target.element.style.top = `${vector.y * newsize / 4}px`;
1384
+ target.element.style.left = `${vector.x * size / 4}px`;
1385
+ target.element.style.top = `${vector.y * size / 4}px`;
1336
1386
  xnew$1.emit('-up', { type: '-up', vector });
1337
1387
  });
1338
1388
  }
1339
1389
  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
- });
1390
+ xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1391
+ xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1348
1392
  const polygons = [
1349
1393
  '<polygon points="32 32 23 23 23 4 24 3 40 3 41 4 41 23">',
1350
1394
  '<polygon points="32 32 23 41 23 60 24 61 40 61 41 60 41 41">',
@@ -1369,10 +1413,11 @@
1369
1413
  xnew$1('<polygon points="57 32 51 27 51 37">');
1370
1414
  });
1371
1415
  unit.on('dragstart dragmove', ({ type, position }) => {
1372
- const x = position.x - newsize / 2;
1373
- const y = position.y - newsize / 2;
1416
+ const size = unit.element.clientWidth;
1417
+ const x = position.x - size / 2;
1418
+ const y = position.y - size / 2;
1374
1419
  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));
1420
+ const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (size / 4));
1376
1421
  const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1377
1422
  if (diagonal === true) {
1378
1423
  vector.x = Math.abs(vector.x) > 0.5 ? Math.sign(vector.x) : 0;
@@ -1391,7 +1436,7 @@
1391
1436
  targets[2].element.style.filter = (vector.x < 0) ? 'brightness(80%)' : '';
1392
1437
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(80%)' : '';
1393
1438
  const nexttype = { dragstart: '-down', dragmove: '-move' }[type];
1394
- xnew$1.emit(nexttype, { type: nexttype, vector });
1439
+ xnew$1.emit(nexttype, { vector });
1395
1440
  });
1396
1441
  unit.on('dragend', () => {
1397
1442
  const vector = { x: 0, y: 0 };
@@ -1399,8 +1444,132 @@
1399
1444
  targets[1].element.style.filter = '';
1400
1445
  targets[2].element.style.filter = '';
1401
1446
  targets[3].element.style.filter = '';
1402
- xnew$1.emit('-up', { type: '-up', vector });
1447
+ xnew$1.emit('-up', { vector });
1448
+ });
1449
+ }
1450
+
1451
+ function Panel(unit, { name, open = false, params }) {
1452
+ const object = params !== null && params !== void 0 ? params : {};
1453
+ xnew$1.extend(Group, { name, open });
1454
+ return {
1455
+ group({ name, open, params }, inner) {
1456
+ const group = xnew$1((unit) => {
1457
+ xnew$1.extend(Panel, { name, open, params: params !== null && params !== void 0 ? params : object });
1458
+ inner(unit);
1459
+ });
1460
+ return group;
1461
+ },
1462
+ button(key) {
1463
+ const button = xnew$1(Button, { key });
1464
+ return button;
1465
+ },
1466
+ select(key, { options = [] } = {}) {
1467
+ var _a, _b;
1468
+ object[key] = (_b = (_a = object[key]) !== null && _a !== void 0 ? _a : options[0]) !== null && _b !== void 0 ? _b : '';
1469
+ const select = xnew$1(Select, { key, value: object[key], options });
1470
+ select.on('input', ({ value }) => object[key] = value);
1471
+ return select;
1472
+ },
1473
+ range(key, options = {}) {
1474
+ var _a, _b;
1475
+ object[key] = (_b = (_a = object[key]) !== null && _a !== void 0 ? _a : options.min) !== null && _b !== void 0 ? _b : 0;
1476
+ const number = xnew$1(Range, Object.assign({ key, value: object[key] }, options));
1477
+ number.on('input', ({ value }) => object[key] = value);
1478
+ return number;
1479
+ },
1480
+ checkbox(key) {
1481
+ var _a;
1482
+ object[key] = (_a = object[key]) !== null && _a !== void 0 ? _a : false;
1483
+ const checkbox = xnew$1(Checkbox, { key, value: object[key] });
1484
+ checkbox.on('input', ({ value }) => object[key] = value);
1485
+ return checkbox;
1486
+ },
1487
+ separator() {
1488
+ xnew$1(Separator);
1489
+ }
1490
+ };
1491
+ }
1492
+ function Group(group, { name, open = false }) {
1493
+ xnew$1.extend(OpenAndClose, { open });
1494
+ if (name) {
1495
+ xnew$1('<div style="height: 2rem; margin: 0.125rem 0; display: flex; align-items: center; cursor: pointer; user-select: none;">', (unit) => {
1496
+ unit.on('click', () => group.toggle());
1497
+ xnew$1('<svg viewBox="0 0 12 12" style="width: 1rem; height: 1rem; margin-right: 0.25rem;" fill="none" stroke="currentColor">', (unit) => {
1498
+ xnew$1('<path d="M6 2 10 6 6 10" />');
1499
+ group.on('-transition', ({ state }) => unit.element.style.transform = `rotate(${state * 90}deg)`);
1500
+ });
1501
+ xnew$1('<div>', name);
1502
+ });
1503
+ }
1504
+ xnew$1.extend(Accordion);
1505
+ }
1506
+ function Button(unit, { key = '' }) {
1507
+ xnew$1.nest('<button style="margin: 0.125rem; height: 2rem; border: 1px solid; border-radius: 0.25rem; cursor: pointer;">');
1508
+ unit.element.textContent = key;
1509
+ unit.on('pointerover', () => {
1510
+ unit.element.style.background = 'color-mix(in srgb, currentColor 5%, transparent)';
1511
+ unit.element.style.borderColor = 'color-mix(in srgb, currentColor 40%, transparent)';
1512
+ });
1513
+ unit.on('pointerout', () => {
1514
+ unit.element.style.background = '';
1515
+ unit.element.style.borderColor = '';
1516
+ });
1517
+ unit.on('pointerdown', () => {
1518
+ unit.element.style.filter = 'brightness(0.5)';
1519
+ });
1520
+ unit.on('pointerup', () => {
1521
+ unit.element.style.filter = '';
1522
+ });
1523
+ }
1524
+ function Separator(unit) {
1525
+ xnew$1.nest('<div style="margin: 0.5rem 0; border-top: 1px solid color-mix(in srgb, currentColor 40%, transparent);">');
1526
+ }
1527
+ function Range(unit, { key = '', value, min = 0, max = 100, step = 1 }) {
1528
+ value = value !== null && value !== void 0 ? value : min;
1529
+ xnew$1.nest(`<div style="position: relative; height: 2rem; margin: 0.125rem 0; cursor: pointer; user-select: none;">`);
1530
+ // fill bar
1531
+ const ratio = (value - min) / (max - min);
1532
+ const fill = xnew$1(`<div style="position: absolute; top: 0; left: 0; bottom: 0; width: ${ratio * 100}%; background: color-mix(in srgb, currentColor 5%, transparent); border: 1px solid color-mix(in srgb, currentColor 40%, transparent); border-radius: 0.25rem; transition: width 0.05s;">`);
1533
+ // overlay labels
1534
+ const status = xnew$1('<div style="position: absolute; inset: 0; padding: 0 0.25rem; display: flex; justify-content: space-between; align-items: center; pointer-events: none;">', (unit) => {
1535
+ xnew$1('<div>', key);
1536
+ xnew$1('<div key="status">', value);
1537
+ });
1538
+ // hidden native input for interaction
1539
+ xnew$1.nest(`<input type="range" name="${key}" min="${min}" max="${max}" step="${step}" value="${value}" style="position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; margin: 0;">`);
1540
+ unit.on('input', ({ event }) => {
1541
+ const v = Number(event.target.value);
1542
+ const r = (v - min) / (max - min);
1543
+ fill.element.style.width = `${r * 100}%`;
1544
+ status.element.querySelector('[key="status"]').textContent = String(v);
1545
+ });
1546
+ }
1547
+ function Checkbox(unit, { key = '', value } = {}) {
1548
+ xnew$1.nest(`<div style="position: relative; height: 2rem; margin: 0.125rem 0; padding: 0 0.25rem; display: flex; align-items: center; cursor: pointer; user-select: none;">`);
1549
+ xnew$1('<div style="flex: 1;">', key);
1550
+ const box = xnew$1('<div style="width: 1.25rem; height: 1.25rem; border: 1px solid color-mix(in srgb, currentColor 40%, transparent); border-radius: 0.25rem; display: flex; align-items: center; justify-content: center; transition: background 0.1s;">', () => {
1551
+ xnew$1('<svg viewBox="0 0 12 12" style="width: 1.25rem; height: 1.25rem; opacity: 0; transition: opacity 0.1s;" fill="none" stroke="color-mix(in srgb, currentColor 80%, transparent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">', () => {
1552
+ xnew$1('<path d="M2 6 5 9 10 3" />');
1553
+ });
1403
1554
  });
1555
+ const check = box.element.querySelector('svg');
1556
+ const update = (checked) => {
1557
+ box.element.style.background = checked ? 'color-mix(in srgb, currentColor 5%, transparent)' : '';
1558
+ check.style.opacity = checked ? '1' : '0';
1559
+ };
1560
+ update(!!value);
1561
+ xnew$1.nest(`<input type="checkbox" name="${key}" ${value ? 'checked' : ''} style="position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; margin: 0;">`);
1562
+ unit.on('input', ({ event, value }) => {
1563
+ update(value);
1564
+ });
1565
+ }
1566
+ function Select(unit, { key = '', value, options = [] } = {}) {
1567
+ xnew$1.nest(`<div style="height: 2rem; margin: 0.125rem 0; padding: 0 0.25rem; display: flex; align-items: center;">`);
1568
+ xnew$1('<div style="flex: 1;">', key);
1569
+ xnew$1.nest(`<select name="${key}" style="height: 2rem; padding: 0.25rem; border: 1px solid color-mix(in srgb, currentColor 40%, transparent); border-radius: 0.25rem; cursor: pointer; user-select: none;">`);
1570
+ for (const option of options) {
1571
+ xnew$1(`<option value="${option}" ${option === value ? 'selected' : ''}>`, option);
1572
+ }
1404
1573
  }
1405
1574
 
1406
1575
  const context = new window.AudioContext();
@@ -1632,6 +1801,9 @@
1632
1801
  OpenAndClose,
1633
1802
  AnalogStick,
1634
1803
  DPad,
1804
+ Panel,
1805
+ Accordion,
1806
+ Modal,
1635
1807
  };
1636
1808
  const audio = {
1637
1809
  load(path) {