@mulsense/xnew 0.7.3 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/xnew.d.ts CHANGED
@@ -60,7 +60,6 @@ declare class Unit {
60
60
  [key: string]: any;
61
61
  _: {
62
62
  parent: Unit | null;
63
- ancestors: Unit[];
64
63
  children: Unit[];
65
64
  state: string;
66
65
  tostart: boolean;
@@ -156,30 +155,40 @@ declare function OpenAndClose(unit: Unit, { open, transition }: {
156
155
  declare function Accordion(unit: Unit): void;
157
156
  declare function Popup(unit: Unit): void;
158
157
 
159
- type ScreenFit = 'contain' | 'cover';
158
+ interface SVGInterface {
159
+ viewBox?: string;
160
+ className?: string;
161
+ style?: string;
162
+ stroke?: string;
163
+ strokeOpacity?: number;
164
+ strokeWidth?: number;
165
+ strokeLinejoin?: string;
166
+ strokeLinecap?: string;
167
+ fill?: string;
168
+ fillOpacity?: number;
169
+ }
170
+ declare function SVG(unit: Unit, { viewBox, className, style, stroke, strokeOpacity, strokeWidth, strokeLinejoin, strokeLinecap, fill, fillOpacity }?: SVGInterface): void;
171
+
160
172
  declare function Screen(unit: Unit, { width, height, fit }?: {
161
173
  width?: number;
162
174
  height?: number;
163
- fit?: ScreenFit;
175
+ fit?: 'contain' | 'cover';
164
176
  }): {
165
177
  readonly canvas: UnitElement;
166
178
  };
167
179
 
168
- declare function AnalogStick(unit: Unit, { stroke, strokeOpacity, strokeWidth, strokeLinejoin, fill, fillOpacity }?: {
180
+ declare function AnalogStick(unit: Unit, { stroke, strokeOpacity, strokeWidth, fill, fillOpacity }?: {
169
181
  stroke?: string;
170
182
  strokeOpacity?: number;
171
183
  strokeWidth?: number;
172
- strokeLinejoin?: string;
173
- diagonal?: boolean;
174
184
  fill?: string;
175
185
  fillOpacity?: number;
176
186
  }): void;
177
- declare function DPad(unit: Unit, { diagonal, stroke, strokeOpacity, strokeWidth, strokeLinejoin, fill, fillOpacity }?: {
187
+ declare function DPad(unit: Unit, { diagonal, stroke, strokeOpacity, strokeWidth, fill, fillOpacity }?: {
178
188
  diagonal?: boolean;
179
189
  stroke?: string;
180
190
  strokeOpacity?: number;
181
191
  strokeWidth?: number;
182
- strokeLinejoin?: string;
183
192
  fill?: string;
184
193
  fillOpacity?: number;
185
194
  }): void;
@@ -213,6 +222,10 @@ declare function Scene(unit: Unit): {
213
222
  append(Component: Function, props?: any): void;
214
223
  };
215
224
 
225
+ declare function VolumeController(unit: Unit, { anchor }?: {
226
+ anchor?: string | undefined;
227
+ }): void;
228
+
216
229
  declare class XImage {
217
230
  canvas: HTMLCanvasElement;
218
231
  constructor(canvas: HTMLCanvasElement);
@@ -291,6 +304,7 @@ declare const xnew: ((...args: UnitArgs) => Unit) & {
291
304
  protect(): void;
292
305
  } & {
293
306
  basics: {
307
+ SVG: typeof SVG;
294
308
  Screen: typeof Screen;
295
309
  OpenAndClose: typeof OpenAndClose;
296
310
  AnalogStick: typeof AnalogStick;
@@ -299,6 +313,7 @@ declare const xnew: ((...args: UnitArgs) => Unit) & {
299
313
  Accordion: typeof Accordion;
300
314
  Popup: typeof Popup;
301
315
  Scene: typeof Scene;
316
+ VolumeController: typeof VolumeController;
302
317
  };
303
318
  audio: {
304
319
  load(path: string): UnitPromise;
package/dist/xnew.js CHANGED
@@ -572,7 +572,6 @@
572
572
  currentContext: baseContext,
573
573
  currentComponent: null,
574
574
  afterSnapshot: null,
575
- ancestors: parent ? [parent, ...parent._.ancestors] : [],
576
575
  children: [],
577
576
  nestElements: [],
578
577
  promises: [],
@@ -668,44 +667,41 @@
668
667
  }
669
668
  static extend(unit, Component, props) {
670
669
  var _a;
671
- if (unit._.Components.includes(Component) === true) {
672
- throw new Error(`The Component is already extended.`);
673
- }
674
- else {
675
- const backupComponent = unit._.currentComponent;
676
- unit._.currentComponent = Component;
677
- if (unit._.parent !== null) {
678
- Unit.addContext(unit._.parent, unit, Component, unit);
679
- }
680
- Unit.addContext(unit, unit, Component, unit);
681
- const defines = (_a = Component(unit, props !== null && props !== void 0 ? props : {})) !== null && _a !== void 0 ? _a : {};
682
- unit._.currentComponent = backupComponent;
683
- Unit.component2units.add(Component, unit);
684
- unit._.Components.push(Component);
685
- Object.keys(defines).forEach((key) => {
686
- if (unit[key] !== undefined && unit._.defines[key] === undefined) {
687
- throw new Error(`The property "${key}" already exists.`);
688
- }
689
- const descriptor = Object.getOwnPropertyDescriptor(defines, key);
690
- const wrapper = { configurable: true, enumerable: true };
691
- const snapshot = Unit.snapshot(unit);
692
- if ((descriptor === null || descriptor === void 0 ? void 0 : descriptor.get) || (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set)) {
693
- if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.get)
694
- wrapper.get = (...args) => Unit.scope(snapshot, descriptor.get, ...args);
695
- if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set)
696
- wrapper.set = (...args) => Unit.scope(snapshot, descriptor.set, ...args);
697
- }
698
- else if (typeof (descriptor === null || descriptor === void 0 ? void 0 : descriptor.value) === 'function') {
699
- wrapper.value = (...args) => Unit.scope(snapshot, descriptor.value, ...args);
700
- }
701
- else {
702
- throw new Error(`Only function properties can be defined as Component defines. [${key}]`);
703
- }
704
- Object.defineProperty(unit._.defines, key, wrapper);
705
- Object.defineProperty(unit, key, wrapper);
706
- });
707
- return Object.assign({}, unit._.defines);
708
- }
670
+ const backupComponent = unit._.currentComponent;
671
+ unit._.currentComponent = Component;
672
+ if (unit._.parent !== null) {
673
+ Unit.addContext(unit._.parent, unit, Component, unit);
674
+ }
675
+ Unit.addContext(unit, unit, Component, unit);
676
+ const defines = (_a = Component(unit, props !== null && props !== void 0 ? props : {})) !== null && _a !== void 0 ? _a : {};
677
+ unit._.currentComponent = backupComponent;
678
+ Unit.component2units.add(Component, unit);
679
+ unit._.Components.push(Component);
680
+ Object.keys(defines).forEach((key) => {
681
+ if (unit[key] !== undefined && unit._.defines[key] === undefined) {
682
+ throw new Error(`The property "${key}" already exists.`);
683
+ }
684
+ const descriptor = Object.getOwnPropertyDescriptor(defines, key);
685
+ const wrapper = { configurable: true, enumerable: true };
686
+ const snapshot = Unit.snapshot(unit);
687
+ if ((descriptor === null || descriptor === void 0 ? void 0 : descriptor.get) || (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set)) {
688
+ if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.get)
689
+ wrapper.get = (...args) => Unit.scope(snapshot, descriptor.get, ...args);
690
+ if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set)
691
+ wrapper.set = (...args) => Unit.scope(snapshot, descriptor.set, ...args);
692
+ }
693
+ else if (typeof (descriptor === null || descriptor === void 0 ? void 0 : descriptor.value) === 'function') {
694
+ wrapper.value = (...args) => Unit.scope(snapshot, descriptor.value, ...args);
695
+ }
696
+ else {
697
+ throw new Error(`Only function properties can be defined as Component defines. [${key}]`);
698
+ }
699
+ Object.defineProperty(unit._.defines, key, wrapper);
700
+ Object.defineProperty(unit, key, wrapper);
701
+ });
702
+ let clone = {};
703
+ Object.defineProperties(clone, Object.getOwnPropertyDescriptors(unit._.defines));
704
+ return clone;
709
705
  }
710
706
  static start(unit) {
711
707
  if (unit._.tostart === false)
@@ -836,10 +832,17 @@
836
832
  var _a, _b;
837
833
  const current = Unit.currentUnit;
838
834
  if (type[0] === '+') {
835
+ const ancestors = [];
836
+ for (let u = current._.parent; u !== null; u = u._.parent)
837
+ ancestors.push(u);
839
838
  (_a = Unit.type2units.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((unit) => {
840
839
  var _a;
841
- const find = [unit, ...unit._.ancestors].find(u => u._.protected === true);
842
- if (find === undefined || current._.ancestors.includes(find) === true || current === find) {
840
+ let find = undefined;
841
+ for (let u = unit; u !== null && find === undefined; u = u._.parent) {
842
+ if (u._.protected === true)
843
+ find = u;
844
+ }
845
+ if (find === undefined || ancestors.includes(find) === true || current === find) {
843
846
  (_a = unit._.listeners.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((item) => item.execute(props));
844
847
  }
845
848
  });
@@ -990,6 +993,9 @@
990
993
  if (Unit.currentUnit._.state !== 'invoked') {
991
994
  throw new Error('xnew.extend can not be called after initialized.');
992
995
  }
996
+ if (Unit.currentUnit._.Components.includes(Component) === true) {
997
+ console.warn('Component is already extended in this unit:', Component);
998
+ }
993
999
  const defines = Unit.extend(Unit.currentUnit, Component, props);
994
1000
  return defines;
995
1001
  }
@@ -1331,10 +1337,24 @@
1331
1337
  });
1332
1338
  }
1333
1339
 
1334
- function Screen(unit, { width = 800, height = 600, fit = 'contain' } = {}) {
1335
- const aspect = width / height;
1336
- xnew$1.nest('<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size; overflow: hidden;">');
1337
- xnew$1.nest(`<div style="position: relative; aspect-ratio: ${aspect}; container-type: size; overflow: hidden;">`);
1340
+ function SVG(unit, { viewBox = '0 0 64 64', className = '', style = '', stroke = 'none', strokeOpacity = 1, strokeWidth = 1, strokeLinejoin = 'round', strokeLinecap = 'round', fill = 'none', fillOpacity = 1 } = {}) {
1341
+ xnew$1.nest(`<svg
1342
+ viewBox="${viewBox}"
1343
+ class="${className}"
1344
+ style="${style}"
1345
+ stroke="${stroke}"
1346
+ stroke-opacity="${strokeOpacity}"
1347
+ stroke-width="${strokeWidth}"
1348
+ stroke-linejoin="${strokeLinejoin}"
1349
+ stroke-linecap="${strokeLinecap}"
1350
+ fill="${fill}"
1351
+ fill-opacity="${fillOpacity}"
1352
+ ">`);
1353
+ }
1354
+
1355
+ function Aspect(unit, { aspect = 1.0, fit = 'contain' } = {}) {
1356
+ xnew$1.nest('<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">');
1357
+ xnew$1.nest(`<div style="position: relative; aspect-ratio: ${aspect}; container-type: size;">`);
1338
1358
  if (fit === 'contain') {
1339
1359
  unit.element.style.width = `min(100cqw, calc(100cqh * ${aspect}))`;
1340
1360
  }
@@ -1342,6 +1362,10 @@
1342
1362
  unit.element.style.flexShrink = '0';
1343
1363
  unit.element.style.width = `max(100cqw, calc(100cqh * ${aspect}))`;
1344
1364
  }
1365
+ }
1366
+
1367
+ function Screen(unit, { width = 800, height = 600, fit = 'contain' } = {}) {
1368
+ xnew$1.extend(Aspect, { aspect: width / height, fit });
1345
1369
  const canvas = xnew$1(`<canvas width="${width}" height="${height}" style="width: 100%; height: 100%; vertical-align: bottom;">`);
1346
1370
  return {
1347
1371
  get canvas() { return canvas.element; },
@@ -1351,26 +1375,19 @@
1351
1375
  //----------------------------------------------------------------------------------------------------
1352
1376
  // controller
1353
1377
  //----------------------------------------------------------------------------------------------------
1354
- function SVGTemplate(self, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = null, fillOpacity = 0.8 }) {
1355
- xnew$1.nest(`<svg
1356
- viewBox="0 0 64 64"
1357
- style="position: absolute; width: 100%; height: 100%; user-select: none; -webkit-user-select: none;
1358
- stroke: ${stroke}; stroke-opacity: ${strokeOpacity}; stroke-width: ${strokeWidth}; stroke-linejoin: ${strokeLinejoin};
1359
- ${fill ? `fill: ${fill}; fill-opacity: ${fillOpacity};` : ''}
1360
- ">`);
1361
- }
1362
- function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1363
- xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1364
- xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: none; pointer-events: auto; overflow: hidden;">`);
1378
+ const svgTemplate = { viewBox: '0 0 64 64', style: "position: absolute; width: 100%; height: 100%;" };
1379
+ function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, fill = '#FFF', fillOpacity = 0.8 } = {}) {
1380
+ xnew$1.extend(Aspect, { aspect: 1.0, fit: 'contain' });
1381
+ xnew$1.nest(`<div style="width: 100%; height: 100%; cursor: pointer; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: none; pointer-events: auto;">`);
1365
1382
  xnew$1((unit) => {
1366
- xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1383
+ xnew$1.extend(SVG, Object.assign(Object.assign({}, svgTemplate), { stroke, strokeOpacity, strokeWidth, fill, fillOpacity }));
1367
1384
  xnew$1('<polygon points="32 7 27 13 37 13">');
1368
1385
  xnew$1('<polygon points="32 57 27 51 37 51">');
1369
1386
  xnew$1('<polygon points=" 7 32 13 27 13 37">');
1370
1387
  xnew$1('<polygon points="57 32 51 27 51 37">');
1371
1388
  });
1372
1389
  const target = xnew$1((unit) => {
1373
- xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1390
+ xnew$1.extend(SVG, Object.assign(Object.assign({}, svgTemplate), { stroke, strokeOpacity, strokeWidth, fill, fillOpacity }));
1374
1391
  xnew$1('<circle cx="32" cy="32" r="14">');
1375
1392
  });
1376
1393
  unit.on('dragstart dragmove', ({ type, position }) => {
@@ -1380,24 +1397,18 @@
1380
1397
  const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (size / 4));
1381
1398
  const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1382
1399
  const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1383
- target.element.style.filter = 'brightness(80%)';
1384
- target.element.style.left = `${vector.x * size / 4}px`;
1385
- target.element.style.top = `${vector.y * size / 4}px`;
1400
+ Object.assign(target.element.style, { filter: 'brightness(80%)', left: `${vector.x * size / 4}px`, top: `${vector.y * size / 4}px` });
1386
1401
  const nexttype = { dragstart: '-down', dragmove: '-move' }[type];
1387
1402
  xnew$1.emit(nexttype, { vector });
1388
1403
  });
1389
1404
  unit.on('dragend', () => {
1390
- const size = unit.element.clientWidth;
1391
- const vector = { x: 0, y: 0 };
1392
- target.element.style.filter = '';
1393
- target.element.style.left = `${vector.x * size / 4}px`;
1394
- target.element.style.top = `${vector.y * size / 4}px`;
1395
- xnew$1.emit('-up', { vector });
1405
+ Object.assign(target.element.style, { filter: '', left: '0px', top: '0px' });
1406
+ xnew$1.emit('-up', { vector: { x: 0, y: 0 } });
1396
1407
  });
1397
1408
  }
1398
- function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1399
- xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1400
- xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: none; pointer-events: auto; overflow: hidden;">`);
1409
+ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, fill = '#FFF', fillOpacity = 0.8 } = {}) {
1410
+ xnew$1.extend(Aspect, { aspect: 1.0, fit: 'contain' });
1411
+ xnew$1.nest(`<div style="width: 100%; height: 100%; cursor: pointer; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: none; pointer-events: auto;">`);
1401
1412
  const polygons = [
1402
1413
  '<polygon points="32 32 23 23 23 4 24 3 40 3 41 4 41 23">',
1403
1414
  '<polygon points="32 32 23 41 23 60 24 61 40 61 41 60 41 41">',
@@ -1406,12 +1417,12 @@
1406
1417
  ];
1407
1418
  const targets = polygons.map((polygon) => {
1408
1419
  return xnew$1((unit) => {
1409
- xnew$1.extend(SVGTemplate, { stroke: 'none', fill, fillOpacity });
1420
+ xnew$1.extend(SVG, Object.assign(Object.assign({}, svgTemplate), { fill, fillOpacity }));
1410
1421
  xnew$1(polygon);
1411
1422
  });
1412
1423
  });
1413
1424
  xnew$1((unit) => {
1414
- xnew$1.extend(SVGTemplate, { fill: 'none', stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1425
+ xnew$1.extend(SVG, Object.assign(Object.assign({}, svgTemplate), { stroke, strokeOpacity, strokeWidth }));
1415
1426
  xnew$1('<polyline points="23 23 23 4 24 3 40 3 41 4 41 23">');
1416
1427
  xnew$1('<polyline points="23 41 23 60 24 61 40 61 41 60 41 41">');
1417
1428
  xnew$1('<polyline points="23 23 4 23 3 24 3 40 4 41 23 41">');
@@ -1448,17 +1459,15 @@
1448
1459
  xnew$1.emit(nexttype, { vector });
1449
1460
  });
1450
1461
  unit.on('dragend', () => {
1451
- const vector = { x: 0, y: 0 };
1452
1462
  targets[0].element.style.filter = '';
1453
1463
  targets[1].element.style.filter = '';
1454
1464
  targets[2].element.style.filter = '';
1455
1465
  targets[3].element.style.filter = '';
1456
- xnew$1.emit('-up', { vector });
1466
+ xnew$1.emit('-up', { vector: { x: 0, y: 0 } });
1457
1467
  });
1458
1468
  }
1459
1469
 
1460
- const currentColorA = 'color-mix(in srgb, currentColor 70%, transparent)';
1461
- const currentColorB = 'color-mix(in srgb, currentColor 10%, transparent)';
1470
+ const paleColor$1 = 'color-mix(in srgb, currentColor 20%, transparent)';
1462
1471
  function Panel(unit, { params }) {
1463
1472
  const object = params !== null && params !== void 0 ? params : {};
1464
1473
  return {
@@ -1505,8 +1514,9 @@
1505
1514
  if (name) {
1506
1515
  xnew$1('<div style="height: 2em; margin: 0.125em 0; display: flex; align-items: center; cursor: pointer; user-select: none;">', (unit) => {
1507
1516
  unit.on('click', () => group.toggle());
1508
- xnew$1('<svg viewBox="0 0 12 12" style="width: 1em; height: 1em; margin-right: 0.25em;" fill="none" stroke="currentColor">', (unit) => {
1509
- xnew$1('<path d="M6 2 10 6 6 10" />');
1517
+ xnew$1((unit) => {
1518
+ xnew$1.extend(SVG, { viewBox: '0 0 12 12', stroke: 'currentColor', style: 'width: 1em; height: 1em; margin-right: 0.25em;' });
1519
+ xnew$1('<path d="M6 2 10 6 6 10"/>');
1510
1520
  group.on('-transition', ({ value }) => unit.element.style.transform = `rotate(${value * 90}deg)`);
1511
1521
  });
1512
1522
  xnew$1('<div>', name);
@@ -1518,12 +1528,10 @@
1518
1528
  xnew$1.nest('<button style="margin: 0.125em 0; height: 2em; border: 1px solid; border-radius: 0.25em; cursor: pointer;">');
1519
1529
  unit.element.textContent = key;
1520
1530
  unit.on('pointerover', () => {
1521
- unit.element.style.background = currentColorB;
1522
- unit.element.style.borderColor = currentColorA;
1531
+ Object.assign(unit.element.style, { background: paleColor$1, borderColor: 'currentColor' });
1523
1532
  });
1524
1533
  unit.on('pointerout', () => {
1525
- unit.element.style.background = '';
1526
- unit.element.style.borderColor = '';
1534
+ Object.assign(unit.element.style, { background: '', borderColor: '' });
1527
1535
  });
1528
1536
  unit.on('pointerdown', () => {
1529
1537
  unit.element.style.filter = 'brightness(0.5)';
@@ -1533,14 +1541,14 @@
1533
1541
  });
1534
1542
  }
1535
1543
  function Separator(unit) {
1536
- xnew$1.nest(`<div style="margin: 0.5em 0; border-top: 1px solid ${currentColorA};">`);
1544
+ xnew$1.nest(`<div style="margin: 0.5em 0; border-top: 1px solid currentColor;">`);
1537
1545
  }
1538
1546
  function Range(unit, { key = '', value, min = 0, max = 100, step = 1 }) {
1539
1547
  value = value !== null && value !== void 0 ? value : min;
1540
1548
  xnew$1.nest(`<div style="position: relative; height: 2em; margin: 0.125em 0; cursor: pointer; user-select: none;">`);
1541
1549
  // fill bar
1542
1550
  const ratio = (value - min) / (max - min);
1543
- const fill = xnew$1(`<div style="position: absolute; top: 0; left: 0; bottom: 0; width: ${ratio * 100}%; background: ${currentColorB}; border: 1px solid ${currentColorA}; border-radius: 0.25em; transition: width 0.05s;">`);
1551
+ const fill = xnew$1(`<div style="position: absolute; top: 0; left: 0; bottom: 0; width: ${ratio * 100}%; background: ${paleColor$1}; border: 1px solid currentColor; border-radius: 0.25em; transition: width 0.05s;">`);
1544
1552
  // overlay labels
1545
1553
  const status = xnew$1('<div style="position: absolute; inset: 0; padding: 0 0.5em; display: flex; justify-content: space-between; align-items: center; pointer-events: none;">', (unit) => {
1546
1554
  xnew$1('<div>', key);
@@ -1558,14 +1566,15 @@
1558
1566
  function Checkbox(unit, { key = '', value } = {}) {
1559
1567
  xnew$1.nest(`<div style="position: relative; height: 2em; margin: 0.125em 0; padding: 0 0.5em; display: flex; align-items: center; cursor: pointer; user-select: none;">`);
1560
1568
  xnew$1('<div style="flex: 1;">', key);
1561
- const box = xnew$1(`<div style="width: 1.25em; height: 1.25em; border: 1px solid ${currentColorA}; border-radius: 0.25em; display: flex; align-items: center; justify-content: center; transition: background 0.1s;">`, () => {
1562
- xnew$1(`<svg viewBox="0 0 12 12" style="width: 1.25em; height: 1.25em; opacity: 0; transition: opacity 0.1s;" fill="none" stroke="${currentColorA}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">`, () => {
1569
+ const box = xnew$1(`<div style="width: 1.25em; height: 1.25em; border: 1px solid currentColor; border-radius: 0.25em; display: flex; align-items: center; justify-content: center;">`, () => {
1570
+ xnew$1((unit) => {
1571
+ xnew$1.extend(SVG, { viewBox: '0 0 12 12', style: 'width: 1.25em; height: 1.25em; opacity: 0;', stroke: 'currentColor', strokeWidth: 2 });
1563
1572
  xnew$1('<path d="M2 6 5 9 10 3" />');
1564
1573
  });
1565
1574
  });
1566
1575
  const check = box.element.querySelector('svg');
1567
1576
  const update = (checked) => {
1568
- box.element.style.background = checked ? currentColorB : '';
1577
+ box.element.style.background = checked ? paleColor$1 : '';
1569
1578
  check.style.opacity = checked ? '1' : '0';
1570
1579
  };
1571
1580
  update(!!value);
@@ -1584,8 +1593,9 @@
1584
1593
  xnew$1(`<option value="${item}" ${item === initial ? 'selected' : ''}>`, item);
1585
1594
  }
1586
1595
  });
1587
- const button = xnew$1(`<div style="height: 2em; padding: 0 1.5em 0 0.5em; display: flex; align-items: center; border: 1px solid ${currentColorA}; border-radius: 0.25em; cursor: pointer; user-select: none; min-width: 3em; white-space: nowrap;">`, initial);
1588
- xnew$1(`<svg viewBox="0 0 12 12" style="position: absolute; right: 1.0em; width: 0.75em; height: 0.75em; pointer-events: none;" fill="none" stroke="${currentColorA}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">`, () => {
1596
+ const button = xnew$1(`<div style="height: 2em; padding: 0 1.5em 0 0.5em; display: flex; align-items: center; border: 1px solid currentColor; border-radius: 0.25em; cursor: pointer; user-select: none; min-width: 3em; white-space: nowrap;">`, initial);
1597
+ xnew$1((unit) => {
1598
+ xnew$1.extend(SVG, { viewBox: '0 0 12 12', stroke: 'currentColor', strokeWidth: 2, style: 'position: absolute; right: 1.0em; width: 0.75em; height: 0.75em; pointer-events: none;' });
1589
1599
  xnew$1('<path d="M2 4 6 8 10 4" />');
1590
1600
  });
1591
1601
  button.on('click', () => {
@@ -1600,10 +1610,10 @@
1600
1610
  list.element.style.background = getEffectiveBg(button.element);
1601
1611
  });
1602
1612
  xnew$1.extend(Accordion);
1603
- xnew$1.nest(`<div style="position: relative; border: 1px solid ${currentColorA}; border-radius: 0.25em; overflow: hidden;">`);
1613
+ xnew$1.nest(`<div style="position: relative; border: 1px solid currentColor; border-radius: 0.25em; overflow: hidden;">`);
1604
1614
  for (const item of items) {
1605
1615
  const div = xnew$1(`<div style="height: 2em; padding: 0 0.5em; display: flex; align-items: center; cursor: pointer; user-select: none;">`, item);
1606
- div.on('pointerover', () => div.element.style.background = currentColorB);
1616
+ div.on('pointerover', () => div.element.style.background = paleColor$1);
1607
1617
  div.on('pointerout', () => div.element.style.background = '');
1608
1618
  div.on('click', () => {
1609
1619
  button.element.textContent = item;
@@ -1612,14 +1622,12 @@
1612
1622
  list.finalize();
1613
1623
  });
1614
1624
  }
1615
- list.on('click.outside', () => {
1616
- list.finalize();
1617
- });
1625
+ list.on('click.outside', () => list.finalize());
1618
1626
  });
1619
1627
  });
1620
1628
  xnew$1.nest(native.element);
1621
- function getEffectiveBg(el) {
1622
- let current = el.parentElement;
1629
+ function getEffectiveBg(element) {
1630
+ let current = element.parentElement;
1623
1631
  while (current) {
1624
1632
  const bg = getComputedStyle(current).backgroundColor;
1625
1633
  if (bg && bg !== 'rgba(0, 0, 0, 0)' && bg !== 'transparent')
@@ -1642,34 +1650,6 @@
1642
1650
  };
1643
1651
  }
1644
1652
 
1645
- class XImage {
1646
- constructor(...args) {
1647
- if (args[0] instanceof HTMLCanvasElement) {
1648
- this.canvas = args[0];
1649
- }
1650
- else {
1651
- const canvas = document.createElement('canvas');
1652
- canvas.width = args[0];
1653
- canvas.height = args[1];
1654
- this.canvas = canvas;
1655
- }
1656
- }
1657
- crop(x, y, width, height) {
1658
- var _a;
1659
- const canvas = document.createElement('canvas');
1660
- canvas.width = width;
1661
- canvas.height = height;
1662
- (_a = canvas.getContext('2d')) === null || _a === void 0 ? void 0 : _a.drawImage(this.canvas, x, y, width, height, 0, 0, width, height);
1663
- return new XImage(canvas);
1664
- }
1665
- download(filename) {
1666
- const link = document.createElement('a');
1667
- link.download = filename;
1668
- link.href = this.canvas.toDataURL('image/png');
1669
- link.click();
1670
- }
1671
- }
1672
-
1673
1653
  const context = new window.AudioContext();
1674
1654
  const master = context.createGain();
1675
1655
  //----------------------------------------------------------------------------------------------------
@@ -1894,7 +1874,87 @@
1894
1874
  }
1895
1875
  }
1896
1876
 
1877
+ const paleColor = 'color-mix(in srgb, currentColor 20%, transparent)';
1878
+ function SpeakerIcon(unit, { muted = false } = {}) {
1879
+ xnew$1.extend(SVG, { viewBox: '0 0 24 24', stroke: 'currentColor', strokeWidth: 1.5 });
1880
+ const path = muted
1881
+ ? 'M17.25 9.75L19.5 12m0 0l2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6l4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9 9 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25z'
1882
+ : 'M19.114 5.636a9 9 0 0 1 0 12.728M16.463 8.288a5.25 5.25 0 0 1 0 7.424M6.75 8.25l4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9 9 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25z';
1883
+ xnew$1(`<path d="${path}" />`);
1884
+ }
1885
+ function VolumeController(unit, { anchor = 'left' } = {}) {
1886
+ xnew$1.extend(Aspect, { aspect: 1.0, fit: 'contain' });
1887
+ unit.on('pointerdown', ({ event }) => event.stopPropagation());
1888
+ const system = xnew$1(OpenAndClose, { open: false, transition: { duration: 250, easing: 'ease' } });
1889
+ const button = xnew$1((unit) => {
1890
+ xnew$1.nest('<div style="width: 100%; height: 100%; cursor: pointer;">');
1891
+ unit.on('click', () => system.toggle());
1892
+ let icon = xnew$1(SpeakerIcon, { muted: master.gain.value === 0 });
1893
+ return {
1894
+ update() {
1895
+ icon === null || icon === void 0 ? void 0 : icon.finalize();
1896
+ icon = xnew$1(SpeakerIcon, { muted: master.gain.value === 0 });
1897
+ }
1898
+ };
1899
+ });
1900
+ xnew$1(() => {
1901
+ const isHoriz = anchor === 'left' || anchor === 'right';
1902
+ const unit = isHoriz ? 'cqw' : 'cqh';
1903
+ const fillProp = isHoriz ? 'width' : 'height';
1904
+ const pct = master.gain.value * 100;
1905
+ const outerSize = isHoriz ? `top: 20%; bottom: 20%; width: 0${unit}` : `left: 20%; right: 20%; height: 0${unit}`;
1906
+ const fillSize = isHoriz ? `top: 0; left: 0; bottom: 0; width: ${pct}%; height: 100%` : `bottom: 0; left: 0; right: 0; width: 100%; height: ${pct}%`;
1907
+ const outer = xnew$1.nest(`<div style="position: absolute; ${outerSize};">`);
1908
+ xnew$1.nest(`<div style="position: relative; width: 100%; height: 100%; border: 1px solid currentColor; border-radius: 0.25em; box-sizing: border-box;">`);
1909
+ const fill = xnew$1(`<div style="position: absolute; ${fillSize}; background: ${paleColor};">`);
1910
+ const input = xnew$1(`<input type="range" min="0" max="100" value="${pct}" style="position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; margin: 0;${isHoriz ? '' : ' writing-mode: vertical-lr; direction: rtl;'}">`);
1911
+ const css = (el) => el.style;
1912
+ input.on('input', ({ event }) => {
1913
+ const v = Number(event.target.value);
1914
+ css(fill.element)[fillProp] = `${v}%`;
1915
+ master.gain.value = v / 100;
1916
+ button.update();
1917
+ });
1918
+ system.on('-transition', ({ value }) => {
1919
+ css(outer)[anchor] = `-${value * 400 + 20}${unit}`;
1920
+ css(outer)[fillProp] = `${value * 400}${unit}`;
1921
+ outer.style.opacity = value.toString();
1922
+ outer.style.pointerEvents = value < 0.9 ? 'none' : 'auto';
1923
+ });
1924
+ });
1925
+ unit.on('click.outside', () => system.close());
1926
+ }
1927
+
1928
+ class XImage {
1929
+ constructor(...args) {
1930
+ if (args[0] instanceof HTMLCanvasElement) {
1931
+ this.canvas = args[0];
1932
+ }
1933
+ else {
1934
+ const canvas = document.createElement('canvas');
1935
+ canvas.width = args[0];
1936
+ canvas.height = args[1];
1937
+ this.canvas = canvas;
1938
+ }
1939
+ }
1940
+ crop(x, y, width, height) {
1941
+ var _a;
1942
+ const canvas = document.createElement('canvas');
1943
+ canvas.width = width;
1944
+ canvas.height = height;
1945
+ (_a = canvas.getContext('2d')) === null || _a === void 0 ? void 0 : _a.drawImage(this.canvas, x, y, width, height, 0, 0, width, height);
1946
+ return new XImage(canvas);
1947
+ }
1948
+ download(filename) {
1949
+ const link = document.createElement('a');
1950
+ link.download = filename;
1951
+ link.href = this.canvas.toDataURL('image/png');
1952
+ link.click();
1953
+ }
1954
+ }
1955
+
1897
1956
  const basics = {
1957
+ SVG,
1898
1958
  Screen,
1899
1959
  OpenAndClose,
1900
1960
  AnalogStick,
@@ -1903,6 +1963,7 @@
1903
1963
  Accordion,
1904
1964
  Popup,
1905
1965
  Scene,
1966
+ VolumeController,
1906
1967
  };
1907
1968
  const audio = {
1908
1969
  load(path) {
package/dist/xnew.mjs CHANGED
@@ -566,7 +566,6 @@ class Unit {
566
566
  currentContext: baseContext,
567
567
  currentComponent: null,
568
568
  afterSnapshot: null,
569
- ancestors: parent ? [parent, ...parent._.ancestors] : [],
570
569
  children: [],
571
570
  nestElements: [],
572
571
  promises: [],
@@ -662,44 +661,41 @@ class Unit {
662
661
  }
663
662
  static extend(unit, Component, props) {
664
663
  var _a;
665
- if (unit._.Components.includes(Component) === true) {
666
- throw new Error(`The Component is already extended.`);
667
- }
668
- else {
669
- const backupComponent = unit._.currentComponent;
670
- unit._.currentComponent = Component;
671
- if (unit._.parent !== null) {
672
- Unit.addContext(unit._.parent, unit, Component, unit);
673
- }
674
- Unit.addContext(unit, unit, Component, unit);
675
- const defines = (_a = Component(unit, props !== null && props !== void 0 ? props : {})) !== null && _a !== void 0 ? _a : {};
676
- unit._.currentComponent = backupComponent;
677
- Unit.component2units.add(Component, unit);
678
- unit._.Components.push(Component);
679
- Object.keys(defines).forEach((key) => {
680
- if (unit[key] !== undefined && unit._.defines[key] === undefined) {
681
- throw new Error(`The property "${key}" already exists.`);
682
- }
683
- const descriptor = Object.getOwnPropertyDescriptor(defines, key);
684
- const wrapper = { configurable: true, enumerable: true };
685
- const snapshot = Unit.snapshot(unit);
686
- if ((descriptor === null || descriptor === void 0 ? void 0 : descriptor.get) || (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set)) {
687
- if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.get)
688
- wrapper.get = (...args) => Unit.scope(snapshot, descriptor.get, ...args);
689
- if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set)
690
- wrapper.set = (...args) => Unit.scope(snapshot, descriptor.set, ...args);
691
- }
692
- else if (typeof (descriptor === null || descriptor === void 0 ? void 0 : descriptor.value) === 'function') {
693
- wrapper.value = (...args) => Unit.scope(snapshot, descriptor.value, ...args);
694
- }
695
- else {
696
- throw new Error(`Only function properties can be defined as Component defines. [${key}]`);
697
- }
698
- Object.defineProperty(unit._.defines, key, wrapper);
699
- Object.defineProperty(unit, key, wrapper);
700
- });
701
- return Object.assign({}, unit._.defines);
702
- }
664
+ const backupComponent = unit._.currentComponent;
665
+ unit._.currentComponent = Component;
666
+ if (unit._.parent !== null) {
667
+ Unit.addContext(unit._.parent, unit, Component, unit);
668
+ }
669
+ Unit.addContext(unit, unit, Component, unit);
670
+ const defines = (_a = Component(unit, props !== null && props !== void 0 ? props : {})) !== null && _a !== void 0 ? _a : {};
671
+ unit._.currentComponent = backupComponent;
672
+ Unit.component2units.add(Component, unit);
673
+ unit._.Components.push(Component);
674
+ Object.keys(defines).forEach((key) => {
675
+ if (unit[key] !== undefined && unit._.defines[key] === undefined) {
676
+ throw new Error(`The property "${key}" already exists.`);
677
+ }
678
+ const descriptor = Object.getOwnPropertyDescriptor(defines, key);
679
+ const wrapper = { configurable: true, enumerable: true };
680
+ const snapshot = Unit.snapshot(unit);
681
+ if ((descriptor === null || descriptor === void 0 ? void 0 : descriptor.get) || (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set)) {
682
+ if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.get)
683
+ wrapper.get = (...args) => Unit.scope(snapshot, descriptor.get, ...args);
684
+ if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set)
685
+ wrapper.set = (...args) => Unit.scope(snapshot, descriptor.set, ...args);
686
+ }
687
+ else if (typeof (descriptor === null || descriptor === void 0 ? void 0 : descriptor.value) === 'function') {
688
+ wrapper.value = (...args) => Unit.scope(snapshot, descriptor.value, ...args);
689
+ }
690
+ else {
691
+ throw new Error(`Only function properties can be defined as Component defines. [${key}]`);
692
+ }
693
+ Object.defineProperty(unit._.defines, key, wrapper);
694
+ Object.defineProperty(unit, key, wrapper);
695
+ });
696
+ let clone = {};
697
+ Object.defineProperties(clone, Object.getOwnPropertyDescriptors(unit._.defines));
698
+ return clone;
703
699
  }
704
700
  static start(unit) {
705
701
  if (unit._.tostart === false)
@@ -830,10 +826,17 @@ class Unit {
830
826
  var _a, _b;
831
827
  const current = Unit.currentUnit;
832
828
  if (type[0] === '+') {
829
+ const ancestors = [];
830
+ for (let u = current._.parent; u !== null; u = u._.parent)
831
+ ancestors.push(u);
833
832
  (_a = Unit.type2units.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((unit) => {
834
833
  var _a;
835
- const find = [unit, ...unit._.ancestors].find(u => u._.protected === true);
836
- if (find === undefined || current._.ancestors.includes(find) === true || current === find) {
834
+ let find = undefined;
835
+ for (let u = unit; u !== null && find === undefined; u = u._.parent) {
836
+ if (u._.protected === true)
837
+ find = u;
838
+ }
839
+ if (find === undefined || ancestors.includes(find) === true || current === find) {
837
840
  (_a = unit._.listeners.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((item) => item.execute(props));
838
841
  }
839
842
  });
@@ -984,6 +987,9 @@ function (...args) {
984
987
  if (Unit.currentUnit._.state !== 'invoked') {
985
988
  throw new Error('xnew.extend can not be called after initialized.');
986
989
  }
990
+ if (Unit.currentUnit._.Components.includes(Component) === true) {
991
+ console.warn('Component is already extended in this unit:', Component);
992
+ }
987
993
  const defines = Unit.extend(Unit.currentUnit, Component, props);
988
994
  return defines;
989
995
  }
@@ -1325,10 +1331,24 @@ function Popup(unit) {
1325
1331
  });
1326
1332
  }
1327
1333
 
1328
- function Screen(unit, { width = 800, height = 600, fit = 'contain' } = {}) {
1329
- const aspect = width / height;
1330
- xnew$1.nest('<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size; overflow: hidden;">');
1331
- xnew$1.nest(`<div style="position: relative; aspect-ratio: ${aspect}; container-type: size; overflow: hidden;">`);
1334
+ function SVG(unit, { viewBox = '0 0 64 64', className = '', style = '', stroke = 'none', strokeOpacity = 1, strokeWidth = 1, strokeLinejoin = 'round', strokeLinecap = 'round', fill = 'none', fillOpacity = 1 } = {}) {
1335
+ xnew$1.nest(`<svg
1336
+ viewBox="${viewBox}"
1337
+ class="${className}"
1338
+ style="${style}"
1339
+ stroke="${stroke}"
1340
+ stroke-opacity="${strokeOpacity}"
1341
+ stroke-width="${strokeWidth}"
1342
+ stroke-linejoin="${strokeLinejoin}"
1343
+ stroke-linecap="${strokeLinecap}"
1344
+ fill="${fill}"
1345
+ fill-opacity="${fillOpacity}"
1346
+ ">`);
1347
+ }
1348
+
1349
+ function Aspect(unit, { aspect = 1.0, fit = 'contain' } = {}) {
1350
+ xnew$1.nest('<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">');
1351
+ xnew$1.nest(`<div style="position: relative; aspect-ratio: ${aspect}; container-type: size;">`);
1332
1352
  if (fit === 'contain') {
1333
1353
  unit.element.style.width = `min(100cqw, calc(100cqh * ${aspect}))`;
1334
1354
  }
@@ -1336,6 +1356,10 @@ function Screen(unit, { width = 800, height = 600, fit = 'contain' } = {}) {
1336
1356
  unit.element.style.flexShrink = '0';
1337
1357
  unit.element.style.width = `max(100cqw, calc(100cqh * ${aspect}))`;
1338
1358
  }
1359
+ }
1360
+
1361
+ function Screen(unit, { width = 800, height = 600, fit = 'contain' } = {}) {
1362
+ xnew$1.extend(Aspect, { aspect: width / height, fit });
1339
1363
  const canvas = xnew$1(`<canvas width="${width}" height="${height}" style="width: 100%; height: 100%; vertical-align: bottom;">`);
1340
1364
  return {
1341
1365
  get canvas() { return canvas.element; },
@@ -1345,26 +1369,19 @@ function Screen(unit, { width = 800, height = 600, fit = 'contain' } = {}) {
1345
1369
  //----------------------------------------------------------------------------------------------------
1346
1370
  // controller
1347
1371
  //----------------------------------------------------------------------------------------------------
1348
- function SVGTemplate(self, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = null, fillOpacity = 0.8 }) {
1349
- xnew$1.nest(`<svg
1350
- viewBox="0 0 64 64"
1351
- style="position: absolute; width: 100%; height: 100%; user-select: none; -webkit-user-select: none;
1352
- stroke: ${stroke}; stroke-opacity: ${strokeOpacity}; stroke-width: ${strokeWidth}; stroke-linejoin: ${strokeLinejoin};
1353
- ${fill ? `fill: ${fill}; fill-opacity: ${fillOpacity};` : ''}
1354
- ">`);
1355
- }
1356
- function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1357
- xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1358
- xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: none; pointer-events: auto; overflow: hidden;">`);
1372
+ const svgTemplate = { viewBox: '0 0 64 64', style: "position: absolute; width: 100%; height: 100%;" };
1373
+ function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, fill = '#FFF', fillOpacity = 0.8 } = {}) {
1374
+ xnew$1.extend(Aspect, { aspect: 1.0, fit: 'contain' });
1375
+ xnew$1.nest(`<div style="width: 100%; height: 100%; cursor: pointer; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: none; pointer-events: auto;">`);
1359
1376
  xnew$1((unit) => {
1360
- xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1377
+ xnew$1.extend(SVG, Object.assign(Object.assign({}, svgTemplate), { stroke, strokeOpacity, strokeWidth, fill, fillOpacity }));
1361
1378
  xnew$1('<polygon points="32 7 27 13 37 13">');
1362
1379
  xnew$1('<polygon points="32 57 27 51 37 51">');
1363
1380
  xnew$1('<polygon points=" 7 32 13 27 13 37">');
1364
1381
  xnew$1('<polygon points="57 32 51 27 51 37">');
1365
1382
  });
1366
1383
  const target = xnew$1((unit) => {
1367
- xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1384
+ xnew$1.extend(SVG, Object.assign(Object.assign({}, svgTemplate), { stroke, strokeOpacity, strokeWidth, fill, fillOpacity }));
1368
1385
  xnew$1('<circle cx="32" cy="32" r="14">');
1369
1386
  });
1370
1387
  unit.on('dragstart dragmove', ({ type, position }) => {
@@ -1374,24 +1391,18 @@ function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strok
1374
1391
  const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (size / 4));
1375
1392
  const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1376
1393
  const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1377
- target.element.style.filter = 'brightness(80%)';
1378
- target.element.style.left = `${vector.x * size / 4}px`;
1379
- target.element.style.top = `${vector.y * size / 4}px`;
1394
+ Object.assign(target.element.style, { filter: 'brightness(80%)', left: `${vector.x * size / 4}px`, top: `${vector.y * size / 4}px` });
1380
1395
  const nexttype = { dragstart: '-down', dragmove: '-move' }[type];
1381
1396
  xnew$1.emit(nexttype, { vector });
1382
1397
  });
1383
1398
  unit.on('dragend', () => {
1384
- const size = unit.element.clientWidth;
1385
- const vector = { x: 0, y: 0 };
1386
- target.element.style.filter = '';
1387
- target.element.style.left = `${vector.x * size / 4}px`;
1388
- target.element.style.top = `${vector.y * size / 4}px`;
1389
- xnew$1.emit('-up', { vector });
1399
+ Object.assign(target.element.style, { filter: '', left: '0px', top: '0px' });
1400
+ xnew$1.emit('-up', { vector: { x: 0, y: 0 } });
1390
1401
  });
1391
1402
  }
1392
- function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, strokeLinejoin = 'round', fill = '#FFF', fillOpacity = 0.8 } = {}) {
1393
- xnew$1.nest(`<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; container-type: size;">`);
1394
- xnew$1.nest(`<div style="width: min(100cqw, 100cqh); aspect-ratio: 1; cursor: pointer; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: none; pointer-events: auto; overflow: hidden;">`);
1403
+ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 1, fill = '#FFF', fillOpacity = 0.8 } = {}) {
1404
+ xnew$1.extend(Aspect, { aspect: 1.0, fit: 'contain' });
1405
+ xnew$1.nest(`<div style="width: 100%; height: 100%; cursor: pointer; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: none; pointer-events: auto;">`);
1395
1406
  const polygons = [
1396
1407
  '<polygon points="32 32 23 23 23 4 24 3 40 3 41 4 41 23">',
1397
1408
  '<polygon points="32 32 23 41 23 60 24 61 40 61 41 60 41 41">',
@@ -1400,12 +1411,12 @@ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity =
1400
1411
  ];
1401
1412
  const targets = polygons.map((polygon) => {
1402
1413
  return xnew$1((unit) => {
1403
- xnew$1.extend(SVGTemplate, { stroke: 'none', fill, fillOpacity });
1414
+ xnew$1.extend(SVG, Object.assign(Object.assign({}, svgTemplate), { fill, fillOpacity }));
1404
1415
  xnew$1(polygon);
1405
1416
  });
1406
1417
  });
1407
1418
  xnew$1((unit) => {
1408
- xnew$1.extend(SVGTemplate, { fill: 'none', stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1419
+ xnew$1.extend(SVG, Object.assign(Object.assign({}, svgTemplate), { stroke, strokeOpacity, strokeWidth }));
1409
1420
  xnew$1('<polyline points="23 23 23 4 24 3 40 3 41 4 41 23">');
1410
1421
  xnew$1('<polyline points="23 41 23 60 24 61 40 61 41 60 41 41">');
1411
1422
  xnew$1('<polyline points="23 23 4 23 3 24 3 40 4 41 23 41">');
@@ -1442,17 +1453,15 @@ function DPad(unit, { diagonal = true, stroke = 'currentColor', strokeOpacity =
1442
1453
  xnew$1.emit(nexttype, { vector });
1443
1454
  });
1444
1455
  unit.on('dragend', () => {
1445
- const vector = { x: 0, y: 0 };
1446
1456
  targets[0].element.style.filter = '';
1447
1457
  targets[1].element.style.filter = '';
1448
1458
  targets[2].element.style.filter = '';
1449
1459
  targets[3].element.style.filter = '';
1450
- xnew$1.emit('-up', { vector });
1460
+ xnew$1.emit('-up', { vector: { x: 0, y: 0 } });
1451
1461
  });
1452
1462
  }
1453
1463
 
1454
- const currentColorA = 'color-mix(in srgb, currentColor 70%, transparent)';
1455
- const currentColorB = 'color-mix(in srgb, currentColor 10%, transparent)';
1464
+ const paleColor$1 = 'color-mix(in srgb, currentColor 20%, transparent)';
1456
1465
  function Panel(unit, { params }) {
1457
1466
  const object = params !== null && params !== void 0 ? params : {};
1458
1467
  return {
@@ -1499,8 +1508,9 @@ function Group(group, { name, open = false }) {
1499
1508
  if (name) {
1500
1509
  xnew$1('<div style="height: 2em; margin: 0.125em 0; display: flex; align-items: center; cursor: pointer; user-select: none;">', (unit) => {
1501
1510
  unit.on('click', () => group.toggle());
1502
- xnew$1('<svg viewBox="0 0 12 12" style="width: 1em; height: 1em; margin-right: 0.25em;" fill="none" stroke="currentColor">', (unit) => {
1503
- xnew$1('<path d="M6 2 10 6 6 10" />');
1511
+ xnew$1((unit) => {
1512
+ xnew$1.extend(SVG, { viewBox: '0 0 12 12', stroke: 'currentColor', style: 'width: 1em; height: 1em; margin-right: 0.25em;' });
1513
+ xnew$1('<path d="M6 2 10 6 6 10"/>');
1504
1514
  group.on('-transition', ({ value }) => unit.element.style.transform = `rotate(${value * 90}deg)`);
1505
1515
  });
1506
1516
  xnew$1('<div>', name);
@@ -1512,12 +1522,10 @@ function Button(unit, { key = '' }) {
1512
1522
  xnew$1.nest('<button style="margin: 0.125em 0; height: 2em; border: 1px solid; border-radius: 0.25em; cursor: pointer;">');
1513
1523
  unit.element.textContent = key;
1514
1524
  unit.on('pointerover', () => {
1515
- unit.element.style.background = currentColorB;
1516
- unit.element.style.borderColor = currentColorA;
1525
+ Object.assign(unit.element.style, { background: paleColor$1, borderColor: 'currentColor' });
1517
1526
  });
1518
1527
  unit.on('pointerout', () => {
1519
- unit.element.style.background = '';
1520
- unit.element.style.borderColor = '';
1528
+ Object.assign(unit.element.style, { background: '', borderColor: '' });
1521
1529
  });
1522
1530
  unit.on('pointerdown', () => {
1523
1531
  unit.element.style.filter = 'brightness(0.5)';
@@ -1527,14 +1535,14 @@ function Button(unit, { key = '' }) {
1527
1535
  });
1528
1536
  }
1529
1537
  function Separator(unit) {
1530
- xnew$1.nest(`<div style="margin: 0.5em 0; border-top: 1px solid ${currentColorA};">`);
1538
+ xnew$1.nest(`<div style="margin: 0.5em 0; border-top: 1px solid currentColor;">`);
1531
1539
  }
1532
1540
  function Range(unit, { key = '', value, min = 0, max = 100, step = 1 }) {
1533
1541
  value = value !== null && value !== void 0 ? value : min;
1534
1542
  xnew$1.nest(`<div style="position: relative; height: 2em; margin: 0.125em 0; cursor: pointer; user-select: none;">`);
1535
1543
  // fill bar
1536
1544
  const ratio = (value - min) / (max - min);
1537
- const fill = xnew$1(`<div style="position: absolute; top: 0; left: 0; bottom: 0; width: ${ratio * 100}%; background: ${currentColorB}; border: 1px solid ${currentColorA}; border-radius: 0.25em; transition: width 0.05s;">`);
1545
+ const fill = xnew$1(`<div style="position: absolute; top: 0; left: 0; bottom: 0; width: ${ratio * 100}%; background: ${paleColor$1}; border: 1px solid currentColor; border-radius: 0.25em; transition: width 0.05s;">`);
1538
1546
  // overlay labels
1539
1547
  const status = xnew$1('<div style="position: absolute; inset: 0; padding: 0 0.5em; display: flex; justify-content: space-between; align-items: center; pointer-events: none;">', (unit) => {
1540
1548
  xnew$1('<div>', key);
@@ -1552,14 +1560,15 @@ function Range(unit, { key = '', value, min = 0, max = 100, step = 1 }) {
1552
1560
  function Checkbox(unit, { key = '', value } = {}) {
1553
1561
  xnew$1.nest(`<div style="position: relative; height: 2em; margin: 0.125em 0; padding: 0 0.5em; display: flex; align-items: center; cursor: pointer; user-select: none;">`);
1554
1562
  xnew$1('<div style="flex: 1;">', key);
1555
- const box = xnew$1(`<div style="width: 1.25em; height: 1.25em; border: 1px solid ${currentColorA}; border-radius: 0.25em; display: flex; align-items: center; justify-content: center; transition: background 0.1s;">`, () => {
1556
- xnew$1(`<svg viewBox="0 0 12 12" style="width: 1.25em; height: 1.25em; opacity: 0; transition: opacity 0.1s;" fill="none" stroke="${currentColorA}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">`, () => {
1563
+ const box = xnew$1(`<div style="width: 1.25em; height: 1.25em; border: 1px solid currentColor; border-radius: 0.25em; display: flex; align-items: center; justify-content: center;">`, () => {
1564
+ xnew$1((unit) => {
1565
+ xnew$1.extend(SVG, { viewBox: '0 0 12 12', style: 'width: 1.25em; height: 1.25em; opacity: 0;', stroke: 'currentColor', strokeWidth: 2 });
1557
1566
  xnew$1('<path d="M2 6 5 9 10 3" />');
1558
1567
  });
1559
1568
  });
1560
1569
  const check = box.element.querySelector('svg');
1561
1570
  const update = (checked) => {
1562
- box.element.style.background = checked ? currentColorB : '';
1571
+ box.element.style.background = checked ? paleColor$1 : '';
1563
1572
  check.style.opacity = checked ? '1' : '0';
1564
1573
  };
1565
1574
  update(!!value);
@@ -1578,8 +1587,9 @@ function Select(_, { key = '', value, items = [] } = {}) {
1578
1587
  xnew$1(`<option value="${item}" ${item === initial ? 'selected' : ''}>`, item);
1579
1588
  }
1580
1589
  });
1581
- const button = xnew$1(`<div style="height: 2em; padding: 0 1.5em 0 0.5em; display: flex; align-items: center; border: 1px solid ${currentColorA}; border-radius: 0.25em; cursor: pointer; user-select: none; min-width: 3em; white-space: nowrap;">`, initial);
1582
- xnew$1(`<svg viewBox="0 0 12 12" style="position: absolute; right: 1.0em; width: 0.75em; height: 0.75em; pointer-events: none;" fill="none" stroke="${currentColorA}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">`, () => {
1590
+ const button = xnew$1(`<div style="height: 2em; padding: 0 1.5em 0 0.5em; display: flex; align-items: center; border: 1px solid currentColor; border-radius: 0.25em; cursor: pointer; user-select: none; min-width: 3em; white-space: nowrap;">`, initial);
1591
+ xnew$1((unit) => {
1592
+ xnew$1.extend(SVG, { viewBox: '0 0 12 12', stroke: 'currentColor', strokeWidth: 2, style: 'position: absolute; right: 1.0em; width: 0.75em; height: 0.75em; pointer-events: none;' });
1583
1593
  xnew$1('<path d="M2 4 6 8 10 4" />');
1584
1594
  });
1585
1595
  button.on('click', () => {
@@ -1594,10 +1604,10 @@ function Select(_, { key = '', value, items = [] } = {}) {
1594
1604
  list.element.style.background = getEffectiveBg(button.element);
1595
1605
  });
1596
1606
  xnew$1.extend(Accordion);
1597
- xnew$1.nest(`<div style="position: relative; border: 1px solid ${currentColorA}; border-radius: 0.25em; overflow: hidden;">`);
1607
+ xnew$1.nest(`<div style="position: relative; border: 1px solid currentColor; border-radius: 0.25em; overflow: hidden;">`);
1598
1608
  for (const item of items) {
1599
1609
  const div = xnew$1(`<div style="height: 2em; padding: 0 0.5em; display: flex; align-items: center; cursor: pointer; user-select: none;">`, item);
1600
- div.on('pointerover', () => div.element.style.background = currentColorB);
1610
+ div.on('pointerover', () => div.element.style.background = paleColor$1);
1601
1611
  div.on('pointerout', () => div.element.style.background = '');
1602
1612
  div.on('click', () => {
1603
1613
  button.element.textContent = item;
@@ -1606,14 +1616,12 @@ function Select(_, { key = '', value, items = [] } = {}) {
1606
1616
  list.finalize();
1607
1617
  });
1608
1618
  }
1609
- list.on('click.outside', () => {
1610
- list.finalize();
1611
- });
1619
+ list.on('click.outside', () => list.finalize());
1612
1620
  });
1613
1621
  });
1614
1622
  xnew$1.nest(native.element);
1615
- function getEffectiveBg(el) {
1616
- let current = el.parentElement;
1623
+ function getEffectiveBg(element) {
1624
+ let current = element.parentElement;
1617
1625
  while (current) {
1618
1626
  const bg = getComputedStyle(current).backgroundColor;
1619
1627
  if (bg && bg !== 'rgba(0, 0, 0, 0)' && bg !== 'transparent')
@@ -1636,34 +1644,6 @@ function Scene(unit) {
1636
1644
  };
1637
1645
  }
1638
1646
 
1639
- class XImage {
1640
- constructor(...args) {
1641
- if (args[0] instanceof HTMLCanvasElement) {
1642
- this.canvas = args[0];
1643
- }
1644
- else {
1645
- const canvas = document.createElement('canvas');
1646
- canvas.width = args[0];
1647
- canvas.height = args[1];
1648
- this.canvas = canvas;
1649
- }
1650
- }
1651
- crop(x, y, width, height) {
1652
- var _a;
1653
- const canvas = document.createElement('canvas');
1654
- canvas.width = width;
1655
- canvas.height = height;
1656
- (_a = canvas.getContext('2d')) === null || _a === void 0 ? void 0 : _a.drawImage(this.canvas, x, y, width, height, 0, 0, width, height);
1657
- return new XImage(canvas);
1658
- }
1659
- download(filename) {
1660
- const link = document.createElement('a');
1661
- link.download = filename;
1662
- link.href = this.canvas.toDataURL('image/png');
1663
- link.click();
1664
- }
1665
- }
1666
-
1667
1647
  const context = new window.AudioContext();
1668
1648
  const master = context.createGain();
1669
1649
  //----------------------------------------------------------------------------------------------------
@@ -1888,7 +1868,87 @@ class Synthesizer {
1888
1868
  }
1889
1869
  }
1890
1870
 
1871
+ const paleColor = 'color-mix(in srgb, currentColor 20%, transparent)';
1872
+ function SpeakerIcon(unit, { muted = false } = {}) {
1873
+ xnew$1.extend(SVG, { viewBox: '0 0 24 24', stroke: 'currentColor', strokeWidth: 1.5 });
1874
+ const path = muted
1875
+ ? 'M17.25 9.75L19.5 12m0 0l2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6l4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9 9 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25z'
1876
+ : 'M19.114 5.636a9 9 0 0 1 0 12.728M16.463 8.288a5.25 5.25 0 0 1 0 7.424M6.75 8.25l4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9 9 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25z';
1877
+ xnew$1(`<path d="${path}" />`);
1878
+ }
1879
+ function VolumeController(unit, { anchor = 'left' } = {}) {
1880
+ xnew$1.extend(Aspect, { aspect: 1.0, fit: 'contain' });
1881
+ unit.on('pointerdown', ({ event }) => event.stopPropagation());
1882
+ const system = xnew$1(OpenAndClose, { open: false, transition: { duration: 250, easing: 'ease' } });
1883
+ const button = xnew$1((unit) => {
1884
+ xnew$1.nest('<div style="width: 100%; height: 100%; cursor: pointer;">');
1885
+ unit.on('click', () => system.toggle());
1886
+ let icon = xnew$1(SpeakerIcon, { muted: master.gain.value === 0 });
1887
+ return {
1888
+ update() {
1889
+ icon === null || icon === void 0 ? void 0 : icon.finalize();
1890
+ icon = xnew$1(SpeakerIcon, { muted: master.gain.value === 0 });
1891
+ }
1892
+ };
1893
+ });
1894
+ xnew$1(() => {
1895
+ const isHoriz = anchor === 'left' || anchor === 'right';
1896
+ const unit = isHoriz ? 'cqw' : 'cqh';
1897
+ const fillProp = isHoriz ? 'width' : 'height';
1898
+ const pct = master.gain.value * 100;
1899
+ const outerSize = isHoriz ? `top: 20%; bottom: 20%; width: 0${unit}` : `left: 20%; right: 20%; height: 0${unit}`;
1900
+ const fillSize = isHoriz ? `top: 0; left: 0; bottom: 0; width: ${pct}%; height: 100%` : `bottom: 0; left: 0; right: 0; width: 100%; height: ${pct}%`;
1901
+ const outer = xnew$1.nest(`<div style="position: absolute; ${outerSize};">`);
1902
+ xnew$1.nest(`<div style="position: relative; width: 100%; height: 100%; border: 1px solid currentColor; border-radius: 0.25em; box-sizing: border-box;">`);
1903
+ const fill = xnew$1(`<div style="position: absolute; ${fillSize}; background: ${paleColor};">`);
1904
+ const input = xnew$1(`<input type="range" min="0" max="100" value="${pct}" style="position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; margin: 0;${isHoriz ? '' : ' writing-mode: vertical-lr; direction: rtl;'}">`);
1905
+ const css = (el) => el.style;
1906
+ input.on('input', ({ event }) => {
1907
+ const v = Number(event.target.value);
1908
+ css(fill.element)[fillProp] = `${v}%`;
1909
+ master.gain.value = v / 100;
1910
+ button.update();
1911
+ });
1912
+ system.on('-transition', ({ value }) => {
1913
+ css(outer)[anchor] = `-${value * 400 + 20}${unit}`;
1914
+ css(outer)[fillProp] = `${value * 400}${unit}`;
1915
+ outer.style.opacity = value.toString();
1916
+ outer.style.pointerEvents = value < 0.9 ? 'none' : 'auto';
1917
+ });
1918
+ });
1919
+ unit.on('click.outside', () => system.close());
1920
+ }
1921
+
1922
+ class XImage {
1923
+ constructor(...args) {
1924
+ if (args[0] instanceof HTMLCanvasElement) {
1925
+ this.canvas = args[0];
1926
+ }
1927
+ else {
1928
+ const canvas = document.createElement('canvas');
1929
+ canvas.width = args[0];
1930
+ canvas.height = args[1];
1931
+ this.canvas = canvas;
1932
+ }
1933
+ }
1934
+ crop(x, y, width, height) {
1935
+ var _a;
1936
+ const canvas = document.createElement('canvas');
1937
+ canvas.width = width;
1938
+ canvas.height = height;
1939
+ (_a = canvas.getContext('2d')) === null || _a === void 0 ? void 0 : _a.drawImage(this.canvas, x, y, width, height, 0, 0, width, height);
1940
+ return new XImage(canvas);
1941
+ }
1942
+ download(filename) {
1943
+ const link = document.createElement('a');
1944
+ link.download = filename;
1945
+ link.href = this.canvas.toDataURL('image/png');
1946
+ link.click();
1947
+ }
1948
+ }
1949
+
1891
1950
  const basics = {
1951
+ SVG,
1892
1952
  Screen,
1893
1953
  OpenAndClose,
1894
1954
  AnalogStick,
@@ -1897,6 +1957,7 @@ const basics = {
1897
1957
  Accordion,
1898
1958
  Popup,
1899
1959
  Scene,
1960
+ VolumeController,
1900
1961
  };
1901
1962
  const audio = {
1902
1963
  load(path) {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "keywords": [
5
5
  "Component-Oriented Programming"
6
6
  ],
7
- "version": "0.7.3",
7
+ "version": "0.7.4",
8
8
  "main": "dist/xnew.js",
9
9
  "module": "dist/xnew.mjs",
10
10
  "types": "dist/xnew.d.ts",