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