@mulsense/xnew 0.6.8 → 0.7.0

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/README.md CHANGED
@@ -13,7 +13,7 @@ providing a flexible architecture well-suited for applications with dynamic scen
13
13
  ### Via CDN
14
14
  Include the following script in your HTML file:
15
15
  ```html
16
- <script src="https://unpkg.com/@mulsense/xnew@0.6.x/dist/xnew.js"></script>
16
+ <script src="https://unpkg.com/@mulsense/xnew@0.7.x/dist/xnew.js"></script>
17
17
  ```
18
18
 
19
19
  ### Via CDN (ESM)
@@ -22,7 +22,7 @@ Use the ES module version with an import map:
22
22
  <script type="importmap">
23
23
  {
24
24
  "imports": {
25
- "@mulsense/xnew": "https://unpkg.com/@mulsense/xnew@0.6.x/dist/xnew.mjs"
25
+ "@mulsense/xnew": "https://unpkg.com/@mulsense/xnew@0.7.x/dist/xnew.mjs"
26
26
  }
27
27
  }
28
28
  </script>
@@ -37,7 +37,7 @@ import xnew from '@mulsense/xnew';
37
37
  ### Via npm
38
38
  Install `xnew` using npm:
39
39
  ```bash
40
- npm install @mulsense/xnew@0.6.x
40
+ npm install @mulsense/xnew@0.7.x
41
41
  ```
42
42
 
43
43
  Then import it in your JavaScript file:
@@ -29,6 +29,11 @@
29
29
  },
30
30
  nest(object) {
31
31
  xnew(Nest, { object });
32
+ xnew.extend(() => {
33
+ return {
34
+ get pixiObject() { return object; }
35
+ };
36
+ });
32
37
  return object;
33
38
  },
34
39
  get renderer() {
@@ -7,6 +7,11 @@ var xpixi = {
7
7
  },
8
8
  nest(object) {
9
9
  xnew(Nest, { object });
10
+ xnew.extend(() => {
11
+ return {
12
+ get pixiObject() { return object; }
13
+ };
14
+ });
10
15
  return object;
11
16
  },
12
17
  get renderer() {
@@ -29,6 +29,11 @@
29
29
  },
30
30
  nest(object) {
31
31
  xnew(Nest, { object });
32
+ xnew.extend(() => {
33
+ return {
34
+ get threeObject() { return object; }
35
+ };
36
+ });
32
37
  return object;
33
38
  },
34
39
  get renderer() {
@@ -67,6 +72,17 @@
67
72
  parent.add(object);
68
73
  unit.on('finalize', () => {
69
74
  parent.remove(object);
75
+ object.traverse((obj) => {
76
+ if (obj.isMesh) {
77
+ obj.geometry.dispose();
78
+ if (Array.isArray(obj.material)) {
79
+ obj.material.forEach((mat) => mat.dispose());
80
+ }
81
+ else {
82
+ obj.material.dispose();
83
+ }
84
+ }
85
+ });
70
86
  });
71
87
  return {
72
88
  get threeObject() { return object; },
@@ -7,6 +7,11 @@ var xthree = {
7
7
  },
8
8
  nest(object) {
9
9
  xnew(Nest, { object });
10
+ xnew.extend(() => {
11
+ return {
12
+ get threeObject() { return object; }
13
+ };
14
+ });
10
15
  return object;
11
16
  },
12
17
  get renderer() {
@@ -45,6 +50,17 @@ function Nest(unit, { object }) {
45
50
  parent.add(object);
46
51
  unit.on('finalize', () => {
47
52
  parent.remove(object);
53
+ object.traverse((obj) => {
54
+ if (obj.isMesh) {
55
+ obj.geometry.dispose();
56
+ if (Array.isArray(obj.material)) {
57
+ obj.material.forEach((mat) => mat.dispose());
58
+ }
59
+ else {
60
+ obj.material.dispose();
61
+ }
62
+ }
63
+ });
48
64
  });
49
65
  return {
50
66
  get threeObject() { return object; },
package/dist/xnew.d.ts CHANGED
@@ -44,6 +44,7 @@ declare class Eventor {
44
44
  }
45
45
 
46
46
  type UnitElement = HTMLElement | SVGElement;
47
+ type UnitArgs = [Component?: Function | string, props?: Object] | [target: UnitElement | string, Component?: Function | string, props?: Object];
47
48
  interface Context {
48
49
  previous: Context | null;
49
50
  key?: any;
@@ -55,39 +56,38 @@ interface Snapshot {
55
56
  element: UnitElement;
56
57
  Component: Function | null;
57
58
  }
58
- interface Internal {
59
- parent: Unit | null;
60
- ancestors: Unit[];
61
- children: Unit[];
62
- state: string;
63
- tostart: boolean;
64
- protected: boolean;
65
- promises: UnitPromise[];
66
- results: Record<string, any>;
67
- defines: Record<string, any>;
68
- systems: Record<string, {
69
- listener: Function;
70
- execute: Function;
71
- }[]>;
72
- currentElement: UnitElement;
73
- currentContext: Context;
74
- currentComponent: Function | null;
75
- nestElements: {
76
- element: UnitElement;
77
- owned: boolean;
78
- }[];
79
- Components: Function[];
80
- listeners: MapMap<string, Function, {
81
- element: UnitElement;
82
- Component: Function | null;
83
- execute: Function;
84
- }>;
85
- eventor: Eventor;
86
- }
87
59
  declare class Unit {
88
60
  [key: string]: any;
89
- _: Internal;
90
- constructor(parent: Unit | null, target: UnitElement | string | null, Component?: Function | string | number, props?: Object);
61
+ _: {
62
+ parent: Unit | null;
63
+ ancestors: Unit[];
64
+ children: Unit[];
65
+ state: string;
66
+ tostart: boolean;
67
+ protected: boolean;
68
+ promises: UnitPromise[];
69
+ results: Record<string, any>;
70
+ defines: Record<string, any>;
71
+ systems: Record<string, {
72
+ listener: Function;
73
+ execute: Function;
74
+ }[]>;
75
+ currentElement: UnitElement;
76
+ currentContext: Context;
77
+ currentComponent: Function | null;
78
+ nestElements: {
79
+ element: UnitElement;
80
+ owned: boolean;
81
+ }[];
82
+ Components: Function[];
83
+ listeners: MapMap<string, Function, {
84
+ element: UnitElement;
85
+ Component: Function | null;
86
+ execute: Function;
87
+ }>;
88
+ eventor: Eventor;
89
+ };
90
+ constructor(parent: Unit | null, ...args: UnitArgs);
91
91
  get element(): UnitElement;
92
92
  start(): void;
93
93
  stop(): void;
@@ -120,49 +120,26 @@ declare class Unit {
120
120
  static emit(type: string, props?: object): void;
121
121
  }
122
122
  declare class UnitPromise {
123
- promise: Promise<any>;
124
- Component: Function | null;
125
- constructor(promise: Promise<any>, Component: Function | null);
123
+ private promise;
124
+ constructor(promise: Promise<any>);
126
125
  then(callback: Function): UnitPromise;
127
126
  catch(callback: Function): UnitPromise;
128
127
  finally(callback: Function): UnitPromise;
128
+ static all(promises: UnitPromise[]): UnitPromise;
129
129
  private wrap;
130
130
  }
131
131
  declare class UnitTimer {
132
132
  private unit;
133
133
  private queue;
134
134
  clear(): void;
135
- timeout(callback: Function, duration?: number): UnitTimer;
136
- interval(callback: Function, duration?: number, iterations?: number): UnitTimer;
135
+ timeout(timeout: Function, duration?: number): UnitTimer;
136
+ interval(timeout: Function, duration?: number, iterations?: number): UnitTimer;
137
137
  transition(transition: Function, duration?: number, easing?: string): UnitTimer;
138
138
  private static execute;
139
139
  private static next;
140
140
  private static Component;
141
141
  }
142
142
 
143
- interface CreateUnit {
144
- /**
145
- * creates a new Unit component
146
- * @param Component - component function
147
- * @param props - properties for component function
148
- * @returns a new Unit instance
149
- * @example
150
- * const unit = xnew(MyComponent, { data: 0 })
151
- */
152
- (Component?: Function | string, props?: Object): Unit;
153
- /**
154
- * creates a new Unit component
155
- * @param target - HTMLElement | SVGElement, or HTML tag for new element
156
- * @param Component - component function
157
- * @param props - properties for component function
158
- * @returns a new Unit instance
159
- * @example
160
- * const unit = xnew(element, MyComponent, { data: 0 })
161
- * const unit = xnew('<div>', MyComponent, { data: 0 })
162
- */
163
- (target: HTMLElement | SVGElement | string, Component?: Function | string, props?: Object): Unit;
164
- }
165
-
166
143
  interface TransitionOptions {
167
144
  duration?: number;
168
145
  easing?: string;
@@ -230,12 +207,21 @@ declare function Panel(unit: Unit, { params }: PanelOptions): {
230
207
  separator(): void;
231
208
  };
232
209
 
210
+ declare function Scene(unit: Unit): void;
233
211
  declare function Flow(unit: Unit): {
234
212
  get scene(): Unit | null;
235
213
  set scene(value: Unit);
236
- next(Component: Function, props?: any): void;
214
+ next(Component: Function, props?: any, callback?: Function): void;
237
215
  };
238
216
 
217
+ declare class XImage {
218
+ canvas: HTMLCanvasElement;
219
+ constructor(canvas: HTMLCanvasElement);
220
+ constructor(width: number, height: number);
221
+ clip(x: number, y: number, width: number, height: number): XImage;
222
+ download(filename: string): void;
223
+ }
224
+
239
225
  type SynthesizerOptions = {
240
226
  oscillator: OscillatorOptions;
241
227
  amp: AmpOptions;
@@ -280,17 +266,22 @@ declare namespace xnew {
280
266
  type Unit = InstanceType<typeof Unit>;
281
267
  type UnitTimer = InstanceType<typeof UnitTimer>;
282
268
  }
283
- declare const xnew: CreateUnit & {
269
+ declare const xnew: ((...args: UnitArgs) => Unit) & {
284
270
  nest(target: UnitElement | string): HTMLElement | SVGElement;
285
271
  extend(Component: Function, props?: Object): {
286
272
  [key: string]: any;
287
273
  };
274
+ append(parent: Unit, ...args: UnitArgs): void;
288
275
  context(key: any): any;
289
276
  promise(promise: Function | Promise<any> | Unit): UnitPromise;
290
277
  then(callback: Function): UnitPromise;
291
278
  catch(callback: Function): UnitPromise;
292
- commit(object?: Record<string, any>): void;
293
279
  finally(callback: Function): UnitPromise;
280
+ resolvers(): {
281
+ resolve(): void;
282
+ reject(): void;
283
+ };
284
+ output(object?: Record<string, any>): void;
294
285
  scope(callback: any): any;
295
286
  find(Component: Function): Unit[];
296
287
  emit(type: string, ...args: any[]): void;
@@ -308,12 +299,16 @@ declare const xnew: CreateUnit & {
308
299
  Accordion: typeof Accordion;
309
300
  Popup: typeof Popup;
310
301
  Flow: typeof Flow;
302
+ Scene: typeof Scene;
311
303
  };
312
304
  audio: {
313
305
  load(path: string): UnitPromise;
314
306
  synthesizer(props: SynthesizerOptions): Synthesizer;
315
307
  volume: number;
316
308
  };
309
+ image: {
310
+ from(canvas: HTMLCanvasElement): XImage;
311
+ };
317
312
  };
318
313
 
319
314
  export { xnew as default };
package/dist/xnew.js CHANGED
@@ -192,7 +192,7 @@
192
192
  this.id = null;
193
193
  this.time = { start: 0.0, processed: 0.0 };
194
194
  (_b = (_a = this.options).transition) === null || _b === void 0 ? void 0 : _b.call(_a, 1.0);
195
- (_d = (_c = this.options).callback) === null || _d === void 0 ? void 0 : _d.call(_c);
195
+ (_d = (_c = this.options).timeout) === null || _d === void 0 ? void 0 : _d.call(_c);
196
196
  this.clear();
197
197
  }, this.options.duration - this.time.processed);
198
198
  this.time.start = Date.now();
@@ -519,16 +519,26 @@
519
519
  return { position };
520
520
  }
521
521
 
522
- //----------------------------------------------------------------------------------------------------
523
- // utils
524
- //----------------------------------------------------------------------------------------------------
525
522
  const SYSTEM_EVENTS = ['start', 'update', 'render', 'stop', 'finalize'];
526
523
  //----------------------------------------------------------------------------------------------------
527
524
  // unit
528
525
  //----------------------------------------------------------------------------------------------------
529
526
  class Unit {
530
- constructor(parent, target, Component, props) {
527
+ constructor(parent, ...args) {
531
528
  var _a;
529
+ let target;
530
+ let Component;
531
+ let props;
532
+ if (args[0] instanceof HTMLElement || args[0] instanceof SVGElement || typeof args[0] === 'string') {
533
+ target = args[0];
534
+ Component = args[1];
535
+ props = args[2];
536
+ }
537
+ else {
538
+ target = null;
539
+ Component = args[0];
540
+ props = args[1];
541
+ }
532
542
  const backup = Unit.currentUnit;
533
543
  Unit.currentUnit = this;
534
544
  parent === null || parent === void 0 ? void 0 : parent._.children.push(this);
@@ -650,7 +660,7 @@
650
660
  return element;
651
661
  }
652
662
  else {
653
- throw new Error(`xnew.nest: invalid html string [${target}]`);
663
+ throw new Error(`xnew.nest: invalid tag string [${target}]`);
654
664
  }
655
665
  }
656
666
  }
@@ -692,7 +702,7 @@
692
702
  Object.defineProperty(unit._.defines, key, wrapper);
693
703
  Object.defineProperty(unit, key, wrapper);
694
704
  });
695
- return defines;
705
+ return Object.assign({}, unit._.defines);
696
706
  }
697
707
  }
698
708
  static start(unit) {
@@ -729,7 +739,7 @@
729
739
  static reset() {
730
740
  var _a;
731
741
  (_a = Unit.rootUnit) === null || _a === void 0 ? void 0 : _a.finalize();
732
- Unit.currentUnit = Unit.rootUnit = new Unit(null, null);
742
+ Unit.currentUnit = Unit.rootUnit = new Unit(null);
733
743
  const ticker = new AnimationTicker(() => {
734
744
  Unit.start(Unit.rootUnit);
735
745
  Unit.update(Unit.rootUnit);
@@ -769,7 +779,7 @@
769
779
  }
770
780
  static getContext(unit, key) {
771
781
  for (let context = unit._.currentContext; context.previous !== null; context = context.previous) {
772
- if (context.value === Unit.currentUnit && key === Unit.currentUnit._.currentComponent)
782
+ if (context.value === Unit.currentUnit)
773
783
  continue;
774
784
  if (context.key === key)
775
785
  return context.value;
@@ -848,13 +858,13 @@
848
858
  // extensions
849
859
  //----------------------------------------------------------------------------------------------------
850
860
  class UnitPromise {
851
- constructor(promise, Component) {
852
- this.promise = promise;
853
- this.Component = Component;
854
- }
861
+ constructor(promise) { this.promise = promise; }
855
862
  then(callback) { return this.wrap('then', callback); }
856
863
  catch(callback) { return this.wrap('catch', callback); }
857
864
  finally(callback) { return this.wrap('finally', callback); }
865
+ static all(promises) {
866
+ return new UnitPromise(Promise.all(promises.map(p => p.promise)));
867
+ }
858
868
  wrap(key, callback) {
859
869
  const snapshot = Unit.snapshot(Unit.currentUnit);
860
870
  this.promise = this.promise[key]((...args) => Unit.scope(snapshot, callback, ...args));
@@ -872,11 +882,11 @@
872
882
  (_a = this.unit) === null || _a === void 0 ? void 0 : _a.finalize();
873
883
  this.unit = null;
874
884
  }
875
- timeout(callback, duration = 0) {
876
- return UnitTimer.execute(this, { callback, duration }, 1);
885
+ timeout(timeout, duration = 0) {
886
+ return UnitTimer.execute(this, { timeout, duration }, 1);
877
887
  }
878
- interval(callback, duration = 0, iterations = 0) {
879
- return UnitTimer.execute(this, { callback, duration }, iterations);
888
+ interval(timeout, duration = 0, iterations = 0) {
889
+ return UnitTimer.execute(this, { timeout, duration }, iterations);
880
890
  }
881
891
  transition(transition, duration = 0, easing) {
882
892
  return UnitTimer.execute(this, { transition, duration, easing }, 1);
@@ -884,7 +894,7 @@
884
894
  static execute(timer, options, iterations) {
885
895
  const props = { options, iterations, snapshot: Unit.snapshot(Unit.currentUnit) };
886
896
  if (timer.unit === null || timer.unit._.state === 'finalized') {
887
- timer.unit = new Unit(Unit.currentUnit, null, UnitTimer.Component, props);
897
+ timer.unit = new Unit(Unit.currentUnit, UnitTimer.Component, props);
888
898
  }
889
899
  else if (timer.queue.length === 0) {
890
900
  timer.queue.push(props);
@@ -897,18 +907,18 @@
897
907
  }
898
908
  static next(timer) {
899
909
  if (timer.queue.length > 0) {
900
- timer.unit = new Unit(Unit.currentUnit, null, UnitTimer.Component, timer.queue.shift());
910
+ timer.unit = new Unit(Unit.currentUnit, UnitTimer.Component, timer.queue.shift());
901
911
  timer.unit.on('finalize', () => UnitTimer.next(timer));
902
912
  }
903
913
  }
904
914
  static Component(unit, { options, iterations, snapshot }) {
905
915
  let counter = 0;
906
- let timer = new Timer({ callback, transition, duration: options.duration, easing: options.easing });
907
- function callback() {
908
- if (options.callback)
909
- Unit.scope(snapshot, options.callback);
916
+ let timer = new Timer({ timeout, transition, duration: options.duration, easing: options.easing });
917
+ function timeout() {
918
+ if (options.timeout)
919
+ Unit.scope(snapshot, options.timeout);
910
920
  if (iterations <= 0 || counter < iterations - 1) {
911
- timer = new Timer({ callback, transition, duration: options.duration, easing: options.easing });
921
+ timer = new Timer({ timeout, transition, duration: options.duration, easing: options.easing });
912
922
  }
913
923
  else {
914
924
  unit.finalize();
@@ -923,29 +933,31 @@
923
933
  }
924
934
  }
925
935
 
926
- const xnew$1 = Object.assign(function (...args) {
936
+ const xnew$1 = Object.assign(
937
+ /**
938
+ * creates a new Unit component
939
+ * xnew(Component?: Function | string, props?: Object): Unit;
940
+ * xnew(target: HTMLElement | SVGElement | string, Component?: Function | string, props?: Object): Unit;
941
+ * @param target - HTMLElement | SVGElement, or HTML tag for new element
942
+ * @param Component - component function
943
+ * @param props - properties for component function
944
+ * @returns a new Unit instance
945
+ * @example
946
+ * const unit = xnew(MyComponent, { data: 0 })
947
+ * const unit = xnew(element, MyComponent, { data: 0 })
948
+ * const unit = xnew('<div>', MyComponent, { data: 0 })
949
+ */
950
+ function (...args) {
927
951
  if (Unit.rootUnit === undefined)
928
952
  Unit.reset();
929
- let target;
930
- if (args[0] instanceof HTMLElement || args[0] instanceof SVGElement) {
931
- target = args.shift(); // an existing html element
932
- }
933
- else if (typeof args[0] === 'string' && args[0].match(/<((\w+)[^>]*?)\/?>/)) {
934
- target = args.shift();
935
- }
936
- else {
937
- target = null;
938
- }
939
- const Component = args.shift();
940
- const props = args.shift();
941
- const unit = new Unit(Unit.currentUnit, target, Component, props);
942
- return unit;
953
+ return new Unit(Unit.currentUnit, ...args);
943
954
  }, {
944
955
  /**
945
- * Creates a nested HTML/SVG element within the current component
946
- * @param target - HTML or SVG tag string (e.g., '<div class="my-class">', '<span style="color:red">', '<svg viewBox="0 0 24 24">')
947
- * @returns The created HTML/SVG element
948
- * @throws Error if called after component initialization
956
+ * Creates a child HTML/SVG element inside the current component's element.
957
+ * Must be called during component initialization (before setup completes).
958
+ * @param target - An existing HTML/SVG element, or a tag string like `'<div>'`
959
+ * @returns The provided element, or the newly created element
960
+ * @throws Error if called after the component has finished initializing
949
961
  * @example
950
962
  * const div = xnew.nest('<div>')
951
963
  * div.textContent = 'Hello'
@@ -958,7 +970,7 @@
958
970
  return Unit.nest(Unit.currentUnit, target);
959
971
  }
960
972
  catch (error) {
961
- console.error('xnew.nest(target: HTMLElement | SVGElement | string): ', error);
973
+ console.error('xnew.nest(target: UnitElement | string): ', error);
962
974
  throw error;
963
975
  }
964
976
  },
@@ -984,17 +996,25 @@
984
996
  throw error;
985
997
  }
986
998
  },
999
+ append(parent, ...args) {
1000
+ try {
1001
+ new Unit(parent, ...args);
1002
+ }
1003
+ catch (error) {
1004
+ console.error('xnew.append(parent: Unit, ...args: UnitArgs): ', error);
1005
+ throw error;
1006
+ }
1007
+ },
987
1008
  /**
988
- * Gets a context value that can be accessed in follow context
989
- * @param key - component function
990
- * @returns The context value
1009
+ * Gets the Unit instance associated with the given component in the ancestor context chain
1010
+ * @param key - component function used as context key
1011
+ * @returns The Unit instance registered with the given component, or undefined if not found
991
1012
  * @example
992
- * // Create unit
993
- * const a = xnew(A);
994
- * ------------------------------
1013
+ * // Create parent unit with component A
1014
+ * const parent = xnew(A);
995
1015
  *
996
- * // Get context in child
997
- * const a = xnew.context(A)
1016
+ * // Inside a child component, get the parent unit
1017
+ * const parentUnit = xnew.context(A)
998
1018
  */
999
1019
  context(key) {
1000
1020
  try {
@@ -1007,24 +1027,22 @@
1007
1027
  },
1008
1028
  /**
1009
1029
  * Registers a promise with the current component for lifecycle management
1010
- * @param promise - Promise to register
1030
+ * @param promise - A Promise, async function, or Unit to register
1011
1031
  * @returns UnitPromise wrapper for chaining
1012
1032
  * @example
1013
1033
  * xnew.promise(fetchData()).then(data => console.log(data))
1014
1034
  */
1015
1035
  promise(promise) {
1016
1036
  try {
1017
- const Component = Unit.currentUnit._.currentComponent;
1018
1037
  let unitPromise;
1019
1038
  if (promise instanceof Unit) {
1020
- unitPromise = new UnitPromise(Promise.all(promise._.promises.map(p => p.promise)), Component)
1021
- .then(() => promise._.results);
1039
+ unitPromise = UnitPromise.all(promise._.promises).then(() => promise._.results);
1022
1040
  }
1023
1041
  else if (promise instanceof Promise) {
1024
- unitPromise = new UnitPromise(promise, Component);
1042
+ unitPromise = new UnitPromise(promise);
1025
1043
  }
1026
1044
  else {
1027
- unitPromise = new UnitPromise(new Promise(xnew$1.scope(promise)), Component);
1045
+ unitPromise = new UnitPromise(new Promise(xnew$1.scope(promise)));
1028
1046
  }
1029
1047
  Unit.currentUnit._.promises.push(unitPromise);
1030
1048
  return unitPromise;
@@ -1044,8 +1062,7 @@
1044
1062
  then(callback) {
1045
1063
  try {
1046
1064
  const currentUnit = Unit.currentUnit;
1047
- return new UnitPromise(Promise.all(Unit.currentUnit._.promises.map(p => p.promise)), null)
1048
- .then(() => callback(currentUnit._.results));
1065
+ return UnitPromise.all(Unit.currentUnit._.promises).then(() => callback(currentUnit._.results));
1049
1066
  }
1050
1067
  catch (error) {
1051
1068
  console.error('xnew.then(callback: Function): ', error);
@@ -1061,7 +1078,7 @@
1061
1078
  */
1062
1079
  catch(callback) {
1063
1080
  try {
1064
- return new UnitPromise(Promise.all(Unit.currentUnit._.promises.map(p => p.promise)), null)
1081
+ return UnitPromise.all(Unit.currentUnit._.promises)
1065
1082
  .catch(callback);
1066
1083
  }
1067
1084
  catch (error) {
@@ -1070,35 +1087,67 @@
1070
1087
  }
1071
1088
  },
1072
1089
  /**
1073
- * Commits a value to the current unit's promise results
1074
- * @param object - object to commit to the promise
1075
- * @returns void
1090
+ * Executes callback after all registered promises settle (resolve or reject)
1091
+ * @param callback - Function to call after promises settle
1092
+ * @returns UnitPromise for chaining
1076
1093
  * @example
1077
- * xnew.commit({ data: 123});
1094
+ * xnew.finally(() => console.log('All promises settled'))
1078
1095
  */
1079
- commit(object) {
1096
+ finally(callback) {
1080
1097
  try {
1081
- Object.assign(Unit.currentUnit._.results, object);
1098
+ return UnitPromise.all(Unit.currentUnit._.promises).finally(callback);
1082
1099
  }
1083
1100
  catch (error) {
1084
- console.error('xnew.commit(object?: Record<string, any>): ', error);
1101
+ console.error('xnew.finally(callback: Function): ', error);
1085
1102
  throw error;
1086
1103
  }
1087
1104
  },
1105
+ resolvers() {
1106
+ let state = null;
1107
+ let resolve = null;
1108
+ let reject = null;
1109
+ const unitPromise = new UnitPromise(new Promise((res, rej) => {
1110
+ if (state === 'resolved') {
1111
+ res(null);
1112
+ }
1113
+ else if (state === 'rejected') {
1114
+ rej();
1115
+ }
1116
+ else {
1117
+ resolve = res;
1118
+ reject = rej;
1119
+ state = 'pending';
1120
+ }
1121
+ }));
1122
+ Unit.currentUnit._.promises.push(unitPromise);
1123
+ return {
1124
+ resolve() {
1125
+ if (state === 'pending') {
1126
+ resolve === null || resolve === void 0 ? void 0 : resolve(null);
1127
+ }
1128
+ state = 'resolved';
1129
+ },
1130
+ reject() {
1131
+ if (state === 'pending') {
1132
+ reject === null || reject === void 0 ? void 0 : reject();
1133
+ }
1134
+ state = 'rejected';
1135
+ }
1136
+ };
1137
+ },
1088
1138
  /**
1089
- * Executes callback after all registered promises settle (resolve or reject)
1090
- * @param callback - Function to call after promises settle
1091
- * @returns UnitPromise for chaining
1139
+ * Outputs a value to the current unit's promise results
1140
+ * @param object - object to output for the promise
1141
+ * @returns void
1092
1142
  * @example
1093
- * xnew.finally(() => console.log('All promises settled'))
1143
+ * xnew.output({ data: 123});
1094
1144
  */
1095
- finally(callback) {
1145
+ output(object) {
1096
1146
  try {
1097
- return new UnitPromise(Promise.all(Unit.currentUnit._.promises.map(p => p.promise)), null)
1098
- .finally(callback);
1147
+ Object.assign(Unit.currentUnit._.results, object);
1099
1148
  }
1100
1149
  catch (error) {
1101
- console.error('xnew.finally(callback: Function): ', error);
1150
+ console.error('xnew.output(object?: Record<string, any>): ', error);
1102
1151
  throw error;
1103
1152
  }
1104
1153
  },
@@ -1136,7 +1185,7 @@
1136
1185
  /**
1137
1186
  * Emits a custom event to components
1138
1187
  * @param type - Event type to emit (prefix with '+' for global events, '-' for local events)
1139
- * @param args - Additional arguments to pass to event listeners
1188
+ * @param props - Event properties object to pass to listeners
1140
1189
  * @returns void
1141
1190
  * @example
1142
1191
  * xnew.emit('+globalevent', { data: 123 }); // Global event
@@ -1153,7 +1202,7 @@
1153
1202
  },
1154
1203
  /**
1155
1204
  * Executes a callback once after a delay, managed by component lifecycle
1156
- * @param callback - Function to execute after Duration
1205
+ * @param callback - Function to execute after duration
1157
1206
  * @param duration - Duration in milliseconds
1158
1207
  * @returns Object with clear() method to cancel the timeout
1159
1208
  * @example
@@ -1275,7 +1324,7 @@
1275
1324
  }
1276
1325
  const canvas = xnew$1(`<canvas width="${width}" height="${height}" style="width: 100%; height: 100%; vertical-align: bottom;">`);
1277
1326
  return {
1278
- get canvas() { return canvas.element; }
1327
+ get canvas() { return canvas.element; },
1279
1328
  };
1280
1329
  }
1281
1330
 
@@ -1561,6 +1610,8 @@
1561
1610
  }
1562
1611
  }
1563
1612
 
1613
+ function Scene(unit) {
1614
+ }
1564
1615
  function Flow(unit) {
1565
1616
  let scene = null;
1566
1617
  return {
@@ -1570,15 +1621,52 @@
1570
1621
  get scene() {
1571
1622
  return scene;
1572
1623
  },
1573
- next(Component, props) {
1574
- var _a;
1575
- // scene change
1576
- (_a = unit.scene) === null || _a === void 0 ? void 0 : _a.finalize();
1577
- unit.scene = xnew$1(Component, props);
1578
- }
1624
+ next(Component, props, callback) {
1625
+ callback = callback !== null && callback !== void 0 ? callback : defaultCallback;
1626
+ callback(scene, create);
1627
+ function defaultCallback(current, create) {
1628
+ current === null || current === void 0 ? void 0 : current.finalize();
1629
+ create();
1630
+ }
1631
+ function create() {
1632
+ scene = xnew$1((unit) => {
1633
+ xnew$1.extend(Scene);
1634
+ xnew$1.extend(Component, props);
1635
+ });
1636
+ return scene;
1637
+ }
1638
+ },
1579
1639
  };
1580
1640
  }
1581
1641
 
1642
+ class XImage {
1643
+ constructor(...args) {
1644
+ if (args[0] instanceof HTMLCanvasElement) {
1645
+ this.canvas = args[0];
1646
+ }
1647
+ else {
1648
+ const canvas = document.createElement('canvas');
1649
+ canvas.width = args[0];
1650
+ canvas.height = args[1];
1651
+ this.canvas = canvas;
1652
+ }
1653
+ }
1654
+ clip(x, y, width, height) {
1655
+ var _a;
1656
+ const canvas = document.createElement('canvas');
1657
+ canvas.width = width;
1658
+ canvas.height = height;
1659
+ (_a = canvas.getContext('2d')) === null || _a === void 0 ? void 0 : _a.drawImage(this.canvas, x, y, width, height, 0, 0, width, height);
1660
+ return new XImage(canvas);
1661
+ }
1662
+ download(filename) {
1663
+ const link = document.createElement('a');
1664
+ link.download = filename;
1665
+ link.href = this.canvas.toDataURL('image/png');
1666
+ link.click();
1667
+ }
1668
+ }
1669
+
1582
1670
  const context = new window.AudioContext();
1583
1671
  const master = context.createGain();
1584
1672
  //----------------------------------------------------------------------------------------------------
@@ -1812,6 +1900,7 @@
1812
1900
  Accordion,
1813
1901
  Popup,
1814
1902
  Flow,
1903
+ Scene,
1815
1904
  };
1816
1905
  const audio = {
1817
1906
  load(path) {
@@ -1840,7 +1929,12 @@
1840
1929
  master.gain.value = value;
1841
1930
  }
1842
1931
  };
1843
- const xnew = Object.assign(xnew$1, { basics, audio });
1932
+ const image = {
1933
+ from(canvas) {
1934
+ return new XImage(canvas);
1935
+ }
1936
+ };
1937
+ const xnew = Object.assign(xnew$1, { basics, audio, image });
1844
1938
 
1845
1939
  return xnew;
1846
1940
 
package/dist/xnew.mjs CHANGED
@@ -186,7 +186,7 @@ class Timer {
186
186
  this.id = null;
187
187
  this.time = { start: 0.0, processed: 0.0 };
188
188
  (_b = (_a = this.options).transition) === null || _b === void 0 ? void 0 : _b.call(_a, 1.0);
189
- (_d = (_c = this.options).callback) === null || _d === void 0 ? void 0 : _d.call(_c);
189
+ (_d = (_c = this.options).timeout) === null || _d === void 0 ? void 0 : _d.call(_c);
190
190
  this.clear();
191
191
  }, this.options.duration - this.time.processed);
192
192
  this.time.start = Date.now();
@@ -513,16 +513,26 @@ function pointer(element, event) {
513
513
  return { position };
514
514
  }
515
515
 
516
- //----------------------------------------------------------------------------------------------------
517
- // utils
518
- //----------------------------------------------------------------------------------------------------
519
516
  const SYSTEM_EVENTS = ['start', 'update', 'render', 'stop', 'finalize'];
520
517
  //----------------------------------------------------------------------------------------------------
521
518
  // unit
522
519
  //----------------------------------------------------------------------------------------------------
523
520
  class Unit {
524
- constructor(parent, target, Component, props) {
521
+ constructor(parent, ...args) {
525
522
  var _a;
523
+ let target;
524
+ let Component;
525
+ let props;
526
+ if (args[0] instanceof HTMLElement || args[0] instanceof SVGElement || typeof args[0] === 'string') {
527
+ target = args[0];
528
+ Component = args[1];
529
+ props = args[2];
530
+ }
531
+ else {
532
+ target = null;
533
+ Component = args[0];
534
+ props = args[1];
535
+ }
526
536
  const backup = Unit.currentUnit;
527
537
  Unit.currentUnit = this;
528
538
  parent === null || parent === void 0 ? void 0 : parent._.children.push(this);
@@ -644,7 +654,7 @@ class Unit {
644
654
  return element;
645
655
  }
646
656
  else {
647
- throw new Error(`xnew.nest: invalid html string [${target}]`);
657
+ throw new Error(`xnew.nest: invalid tag string [${target}]`);
648
658
  }
649
659
  }
650
660
  }
@@ -686,7 +696,7 @@ class Unit {
686
696
  Object.defineProperty(unit._.defines, key, wrapper);
687
697
  Object.defineProperty(unit, key, wrapper);
688
698
  });
689
- return defines;
699
+ return Object.assign({}, unit._.defines);
690
700
  }
691
701
  }
692
702
  static start(unit) {
@@ -723,7 +733,7 @@ class Unit {
723
733
  static reset() {
724
734
  var _a;
725
735
  (_a = Unit.rootUnit) === null || _a === void 0 ? void 0 : _a.finalize();
726
- Unit.currentUnit = Unit.rootUnit = new Unit(null, null);
736
+ Unit.currentUnit = Unit.rootUnit = new Unit(null);
727
737
  const ticker = new AnimationTicker(() => {
728
738
  Unit.start(Unit.rootUnit);
729
739
  Unit.update(Unit.rootUnit);
@@ -763,7 +773,7 @@ class Unit {
763
773
  }
764
774
  static getContext(unit, key) {
765
775
  for (let context = unit._.currentContext; context.previous !== null; context = context.previous) {
766
- if (context.value === Unit.currentUnit && key === Unit.currentUnit._.currentComponent)
776
+ if (context.value === Unit.currentUnit)
767
777
  continue;
768
778
  if (context.key === key)
769
779
  return context.value;
@@ -842,13 +852,13 @@ Unit.type2units = new MapSet();
842
852
  // extensions
843
853
  //----------------------------------------------------------------------------------------------------
844
854
  class UnitPromise {
845
- constructor(promise, Component) {
846
- this.promise = promise;
847
- this.Component = Component;
848
- }
855
+ constructor(promise) { this.promise = promise; }
849
856
  then(callback) { return this.wrap('then', callback); }
850
857
  catch(callback) { return this.wrap('catch', callback); }
851
858
  finally(callback) { return this.wrap('finally', callback); }
859
+ static all(promises) {
860
+ return new UnitPromise(Promise.all(promises.map(p => p.promise)));
861
+ }
852
862
  wrap(key, callback) {
853
863
  const snapshot = Unit.snapshot(Unit.currentUnit);
854
864
  this.promise = this.promise[key]((...args) => Unit.scope(snapshot, callback, ...args));
@@ -866,11 +876,11 @@ class UnitTimer {
866
876
  (_a = this.unit) === null || _a === void 0 ? void 0 : _a.finalize();
867
877
  this.unit = null;
868
878
  }
869
- timeout(callback, duration = 0) {
870
- return UnitTimer.execute(this, { callback, duration }, 1);
879
+ timeout(timeout, duration = 0) {
880
+ return UnitTimer.execute(this, { timeout, duration }, 1);
871
881
  }
872
- interval(callback, duration = 0, iterations = 0) {
873
- return UnitTimer.execute(this, { callback, duration }, iterations);
882
+ interval(timeout, duration = 0, iterations = 0) {
883
+ return UnitTimer.execute(this, { timeout, duration }, iterations);
874
884
  }
875
885
  transition(transition, duration = 0, easing) {
876
886
  return UnitTimer.execute(this, { transition, duration, easing }, 1);
@@ -878,7 +888,7 @@ class UnitTimer {
878
888
  static execute(timer, options, iterations) {
879
889
  const props = { options, iterations, snapshot: Unit.snapshot(Unit.currentUnit) };
880
890
  if (timer.unit === null || timer.unit._.state === 'finalized') {
881
- timer.unit = new Unit(Unit.currentUnit, null, UnitTimer.Component, props);
891
+ timer.unit = new Unit(Unit.currentUnit, UnitTimer.Component, props);
882
892
  }
883
893
  else if (timer.queue.length === 0) {
884
894
  timer.queue.push(props);
@@ -891,18 +901,18 @@ class UnitTimer {
891
901
  }
892
902
  static next(timer) {
893
903
  if (timer.queue.length > 0) {
894
- timer.unit = new Unit(Unit.currentUnit, null, UnitTimer.Component, timer.queue.shift());
904
+ timer.unit = new Unit(Unit.currentUnit, UnitTimer.Component, timer.queue.shift());
895
905
  timer.unit.on('finalize', () => UnitTimer.next(timer));
896
906
  }
897
907
  }
898
908
  static Component(unit, { options, iterations, snapshot }) {
899
909
  let counter = 0;
900
- let timer = new Timer({ callback, transition, duration: options.duration, easing: options.easing });
901
- function callback() {
902
- if (options.callback)
903
- Unit.scope(snapshot, options.callback);
910
+ let timer = new Timer({ timeout, transition, duration: options.duration, easing: options.easing });
911
+ function timeout() {
912
+ if (options.timeout)
913
+ Unit.scope(snapshot, options.timeout);
904
914
  if (iterations <= 0 || counter < iterations - 1) {
905
- timer = new Timer({ callback, transition, duration: options.duration, easing: options.easing });
915
+ timer = new Timer({ timeout, transition, duration: options.duration, easing: options.easing });
906
916
  }
907
917
  else {
908
918
  unit.finalize();
@@ -917,29 +927,31 @@ class UnitTimer {
917
927
  }
918
928
  }
919
929
 
920
- const xnew$1 = Object.assign(function (...args) {
930
+ const xnew$1 = Object.assign(
931
+ /**
932
+ * creates a new Unit component
933
+ * xnew(Component?: Function | string, props?: Object): Unit;
934
+ * xnew(target: HTMLElement | SVGElement | string, Component?: Function | string, props?: Object): Unit;
935
+ * @param target - HTMLElement | SVGElement, or HTML tag for new element
936
+ * @param Component - component function
937
+ * @param props - properties for component function
938
+ * @returns a new Unit instance
939
+ * @example
940
+ * const unit = xnew(MyComponent, { data: 0 })
941
+ * const unit = xnew(element, MyComponent, { data: 0 })
942
+ * const unit = xnew('<div>', MyComponent, { data: 0 })
943
+ */
944
+ function (...args) {
921
945
  if (Unit.rootUnit === undefined)
922
946
  Unit.reset();
923
- let target;
924
- if (args[0] instanceof HTMLElement || args[0] instanceof SVGElement) {
925
- target = args.shift(); // an existing html element
926
- }
927
- else if (typeof args[0] === 'string' && args[0].match(/<((\w+)[^>]*?)\/?>/)) {
928
- target = args.shift();
929
- }
930
- else {
931
- target = null;
932
- }
933
- const Component = args.shift();
934
- const props = args.shift();
935
- const unit = new Unit(Unit.currentUnit, target, Component, props);
936
- return unit;
947
+ return new Unit(Unit.currentUnit, ...args);
937
948
  }, {
938
949
  /**
939
- * Creates a nested HTML/SVG element within the current component
940
- * @param target - HTML or SVG tag string (e.g., '<div class="my-class">', '<span style="color:red">', '<svg viewBox="0 0 24 24">')
941
- * @returns The created HTML/SVG element
942
- * @throws Error if called after component initialization
950
+ * Creates a child HTML/SVG element inside the current component's element.
951
+ * Must be called during component initialization (before setup completes).
952
+ * @param target - An existing HTML/SVG element, or a tag string like `'<div>'`
953
+ * @returns The provided element, or the newly created element
954
+ * @throws Error if called after the component has finished initializing
943
955
  * @example
944
956
  * const div = xnew.nest('<div>')
945
957
  * div.textContent = 'Hello'
@@ -952,7 +964,7 @@ const xnew$1 = Object.assign(function (...args) {
952
964
  return Unit.nest(Unit.currentUnit, target);
953
965
  }
954
966
  catch (error) {
955
- console.error('xnew.nest(target: HTMLElement | SVGElement | string): ', error);
967
+ console.error('xnew.nest(target: UnitElement | string): ', error);
956
968
  throw error;
957
969
  }
958
970
  },
@@ -978,17 +990,25 @@ const xnew$1 = Object.assign(function (...args) {
978
990
  throw error;
979
991
  }
980
992
  },
993
+ append(parent, ...args) {
994
+ try {
995
+ new Unit(parent, ...args);
996
+ }
997
+ catch (error) {
998
+ console.error('xnew.append(parent: Unit, ...args: UnitArgs): ', error);
999
+ throw error;
1000
+ }
1001
+ },
981
1002
  /**
982
- * Gets a context value that can be accessed in follow context
983
- * @param key - component function
984
- * @returns The context value
1003
+ * Gets the Unit instance associated with the given component in the ancestor context chain
1004
+ * @param key - component function used as context key
1005
+ * @returns The Unit instance registered with the given component, or undefined if not found
985
1006
  * @example
986
- * // Create unit
987
- * const a = xnew(A);
988
- * ------------------------------
1007
+ * // Create parent unit with component A
1008
+ * const parent = xnew(A);
989
1009
  *
990
- * // Get context in child
991
- * const a = xnew.context(A)
1010
+ * // Inside a child component, get the parent unit
1011
+ * const parentUnit = xnew.context(A)
992
1012
  */
993
1013
  context(key) {
994
1014
  try {
@@ -1001,24 +1021,22 @@ const xnew$1 = Object.assign(function (...args) {
1001
1021
  },
1002
1022
  /**
1003
1023
  * Registers a promise with the current component for lifecycle management
1004
- * @param promise - Promise to register
1024
+ * @param promise - A Promise, async function, or Unit to register
1005
1025
  * @returns UnitPromise wrapper for chaining
1006
1026
  * @example
1007
1027
  * xnew.promise(fetchData()).then(data => console.log(data))
1008
1028
  */
1009
1029
  promise(promise) {
1010
1030
  try {
1011
- const Component = Unit.currentUnit._.currentComponent;
1012
1031
  let unitPromise;
1013
1032
  if (promise instanceof Unit) {
1014
- unitPromise = new UnitPromise(Promise.all(promise._.promises.map(p => p.promise)), Component)
1015
- .then(() => promise._.results);
1033
+ unitPromise = UnitPromise.all(promise._.promises).then(() => promise._.results);
1016
1034
  }
1017
1035
  else if (promise instanceof Promise) {
1018
- unitPromise = new UnitPromise(promise, Component);
1036
+ unitPromise = new UnitPromise(promise);
1019
1037
  }
1020
1038
  else {
1021
- unitPromise = new UnitPromise(new Promise(xnew$1.scope(promise)), Component);
1039
+ unitPromise = new UnitPromise(new Promise(xnew$1.scope(promise)));
1022
1040
  }
1023
1041
  Unit.currentUnit._.promises.push(unitPromise);
1024
1042
  return unitPromise;
@@ -1038,8 +1056,7 @@ const xnew$1 = Object.assign(function (...args) {
1038
1056
  then(callback) {
1039
1057
  try {
1040
1058
  const currentUnit = Unit.currentUnit;
1041
- return new UnitPromise(Promise.all(Unit.currentUnit._.promises.map(p => p.promise)), null)
1042
- .then(() => callback(currentUnit._.results));
1059
+ return UnitPromise.all(Unit.currentUnit._.promises).then(() => callback(currentUnit._.results));
1043
1060
  }
1044
1061
  catch (error) {
1045
1062
  console.error('xnew.then(callback: Function): ', error);
@@ -1055,7 +1072,7 @@ const xnew$1 = Object.assign(function (...args) {
1055
1072
  */
1056
1073
  catch(callback) {
1057
1074
  try {
1058
- return new UnitPromise(Promise.all(Unit.currentUnit._.promises.map(p => p.promise)), null)
1075
+ return UnitPromise.all(Unit.currentUnit._.promises)
1059
1076
  .catch(callback);
1060
1077
  }
1061
1078
  catch (error) {
@@ -1064,35 +1081,67 @@ const xnew$1 = Object.assign(function (...args) {
1064
1081
  }
1065
1082
  },
1066
1083
  /**
1067
- * Commits a value to the current unit's promise results
1068
- * @param object - object to commit to the promise
1069
- * @returns void
1084
+ * Executes callback after all registered promises settle (resolve or reject)
1085
+ * @param callback - Function to call after promises settle
1086
+ * @returns UnitPromise for chaining
1070
1087
  * @example
1071
- * xnew.commit({ data: 123});
1088
+ * xnew.finally(() => console.log('All promises settled'))
1072
1089
  */
1073
- commit(object) {
1090
+ finally(callback) {
1074
1091
  try {
1075
- Object.assign(Unit.currentUnit._.results, object);
1092
+ return UnitPromise.all(Unit.currentUnit._.promises).finally(callback);
1076
1093
  }
1077
1094
  catch (error) {
1078
- console.error('xnew.commit(object?: Record<string, any>): ', error);
1095
+ console.error('xnew.finally(callback: Function): ', error);
1079
1096
  throw error;
1080
1097
  }
1081
1098
  },
1099
+ resolvers() {
1100
+ let state = null;
1101
+ let resolve = null;
1102
+ let reject = null;
1103
+ const unitPromise = new UnitPromise(new Promise((res, rej) => {
1104
+ if (state === 'resolved') {
1105
+ res(null);
1106
+ }
1107
+ else if (state === 'rejected') {
1108
+ rej();
1109
+ }
1110
+ else {
1111
+ resolve = res;
1112
+ reject = rej;
1113
+ state = 'pending';
1114
+ }
1115
+ }));
1116
+ Unit.currentUnit._.promises.push(unitPromise);
1117
+ return {
1118
+ resolve() {
1119
+ if (state === 'pending') {
1120
+ resolve === null || resolve === void 0 ? void 0 : resolve(null);
1121
+ }
1122
+ state = 'resolved';
1123
+ },
1124
+ reject() {
1125
+ if (state === 'pending') {
1126
+ reject === null || reject === void 0 ? void 0 : reject();
1127
+ }
1128
+ state = 'rejected';
1129
+ }
1130
+ };
1131
+ },
1082
1132
  /**
1083
- * Executes callback after all registered promises settle (resolve or reject)
1084
- * @param callback - Function to call after promises settle
1085
- * @returns UnitPromise for chaining
1133
+ * Outputs a value to the current unit's promise results
1134
+ * @param object - object to output for the promise
1135
+ * @returns void
1086
1136
  * @example
1087
- * xnew.finally(() => console.log('All promises settled'))
1137
+ * xnew.output({ data: 123});
1088
1138
  */
1089
- finally(callback) {
1139
+ output(object) {
1090
1140
  try {
1091
- return new UnitPromise(Promise.all(Unit.currentUnit._.promises.map(p => p.promise)), null)
1092
- .finally(callback);
1141
+ Object.assign(Unit.currentUnit._.results, object);
1093
1142
  }
1094
1143
  catch (error) {
1095
- console.error('xnew.finally(callback: Function): ', error);
1144
+ console.error('xnew.output(object?: Record<string, any>): ', error);
1096
1145
  throw error;
1097
1146
  }
1098
1147
  },
@@ -1130,7 +1179,7 @@ const xnew$1 = Object.assign(function (...args) {
1130
1179
  /**
1131
1180
  * Emits a custom event to components
1132
1181
  * @param type - Event type to emit (prefix with '+' for global events, '-' for local events)
1133
- * @param args - Additional arguments to pass to event listeners
1182
+ * @param props - Event properties object to pass to listeners
1134
1183
  * @returns void
1135
1184
  * @example
1136
1185
  * xnew.emit('+globalevent', { data: 123 }); // Global event
@@ -1147,7 +1196,7 @@ const xnew$1 = Object.assign(function (...args) {
1147
1196
  },
1148
1197
  /**
1149
1198
  * Executes a callback once after a delay, managed by component lifecycle
1150
- * @param callback - Function to execute after Duration
1199
+ * @param callback - Function to execute after duration
1151
1200
  * @param duration - Duration in milliseconds
1152
1201
  * @returns Object with clear() method to cancel the timeout
1153
1202
  * @example
@@ -1269,7 +1318,7 @@ function Screen(unit, { width = 800, height = 600, fit = 'contain' } = {}) {
1269
1318
  }
1270
1319
  const canvas = xnew$1(`<canvas width="${width}" height="${height}" style="width: 100%; height: 100%; vertical-align: bottom;">`);
1271
1320
  return {
1272
- get canvas() { return canvas.element; }
1321
+ get canvas() { return canvas.element; },
1273
1322
  };
1274
1323
  }
1275
1324
 
@@ -1555,6 +1604,8 @@ function Select(_, { key = '', value, items = [] } = {}) {
1555
1604
  }
1556
1605
  }
1557
1606
 
1607
+ function Scene(unit) {
1608
+ }
1558
1609
  function Flow(unit) {
1559
1610
  let scene = null;
1560
1611
  return {
@@ -1564,15 +1615,52 @@ function Flow(unit) {
1564
1615
  get scene() {
1565
1616
  return scene;
1566
1617
  },
1567
- next(Component, props) {
1568
- var _a;
1569
- // scene change
1570
- (_a = unit.scene) === null || _a === void 0 ? void 0 : _a.finalize();
1571
- unit.scene = xnew$1(Component, props);
1572
- }
1618
+ next(Component, props, callback) {
1619
+ callback = callback !== null && callback !== void 0 ? callback : defaultCallback;
1620
+ callback(scene, create);
1621
+ function defaultCallback(current, create) {
1622
+ current === null || current === void 0 ? void 0 : current.finalize();
1623
+ create();
1624
+ }
1625
+ function create() {
1626
+ scene = xnew$1((unit) => {
1627
+ xnew$1.extend(Scene);
1628
+ xnew$1.extend(Component, props);
1629
+ });
1630
+ return scene;
1631
+ }
1632
+ },
1573
1633
  };
1574
1634
  }
1575
1635
 
1636
+ class XImage {
1637
+ constructor(...args) {
1638
+ if (args[0] instanceof HTMLCanvasElement) {
1639
+ this.canvas = args[0];
1640
+ }
1641
+ else {
1642
+ const canvas = document.createElement('canvas');
1643
+ canvas.width = args[0];
1644
+ canvas.height = args[1];
1645
+ this.canvas = canvas;
1646
+ }
1647
+ }
1648
+ clip(x, y, width, height) {
1649
+ var _a;
1650
+ const canvas = document.createElement('canvas');
1651
+ canvas.width = width;
1652
+ canvas.height = height;
1653
+ (_a = canvas.getContext('2d')) === null || _a === void 0 ? void 0 : _a.drawImage(this.canvas, x, y, width, height, 0, 0, width, height);
1654
+ return new XImage(canvas);
1655
+ }
1656
+ download(filename) {
1657
+ const link = document.createElement('a');
1658
+ link.download = filename;
1659
+ link.href = this.canvas.toDataURL('image/png');
1660
+ link.click();
1661
+ }
1662
+ }
1663
+
1576
1664
  const context = new window.AudioContext();
1577
1665
  const master = context.createGain();
1578
1666
  //----------------------------------------------------------------------------------------------------
@@ -1806,6 +1894,7 @@ const basics = {
1806
1894
  Accordion,
1807
1895
  Popup,
1808
1896
  Flow,
1897
+ Scene,
1809
1898
  };
1810
1899
  const audio = {
1811
1900
  load(path) {
@@ -1834,6 +1923,11 @@ const audio = {
1834
1923
  master.gain.value = value;
1835
1924
  }
1836
1925
  };
1837
- const xnew = Object.assign(xnew$1, { basics, audio });
1926
+ const image = {
1927
+ from(canvas) {
1928
+ return new XImage(canvas);
1929
+ }
1930
+ };
1931
+ const xnew = Object.assign(xnew$1, { basics, audio, image });
1838
1932
 
1839
1933
  export { xnew as default };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "keywords": [
5
5
  "Component-Oriented Programming"
6
6
  ],
7
- "version": "0.6.8",
7
+ "version": "0.7.0",
8
8
  "main": "dist/xnew.js",
9
9
  "module": "dist/xnew.mjs",
10
10
  "types": "dist/xnew.d.ts",