x4js 2.0.14 → 2.0.17

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.
Files changed (75) hide show
  1. package/lib/cjs/x4.css +1 -1
  2. package/lib/cjs/x4.js +2 -2
  3. package/lib/esm/x4.css +1 -1
  4. package/lib/esm/x4.mjs +2 -2
  5. package/lib/src/components/boxes/boxes.ts +101 -9
  6. package/lib/src/components/canvas/canvas_ex.ts +13 -6
  7. package/lib/src/components/components.ts +2 -1
  8. package/lib/src/components/dialog/dialog.module.scss +3 -4
  9. package/lib/src/components/dialog/dialog.ts +19 -2
  10. package/lib/src/components/form/form.ts +1 -1
  11. package/lib/src/components/icon/icon.module.scss +1 -0
  12. package/lib/src/components/icon/icon.ts +0 -2
  13. package/lib/src/components/input/input.ts +46 -22
  14. package/lib/src/components/keyboard/keyboard.ts +29 -28
  15. package/lib/src/components/menu/menu.ts +2 -2
  16. package/lib/src/components/popup/popup.module.scss +6 -0
  17. package/lib/src/components/popup/popup.ts +5 -3
  18. package/lib/src/components/propgrid/progrid.module.scss +4 -0
  19. package/lib/src/components/propgrid/propgrid.ts +22 -5
  20. package/lib/src/components/sizers/sizer.ts +2 -1
  21. package/lib/src/components/tabs/tabs.ts +6 -0
  22. package/lib/src/components/textedit/textedit.ts +26 -21
  23. package/lib/src/core/component.ts +15 -21
  24. package/lib/src/core/core_application.ts +2 -1
  25. package/lib/src/core/core_data.ts +9 -8
  26. package/lib/src/core/core_element.ts +5 -0
  27. package/lib/src/core/core_i18n.ts +1 -1
  28. package/lib/src/core/core_router.ts +2 -2
  29. package/lib/src/core/core_state.ts +62 -0
  30. package/lib/src/core/core_svg.ts +1 -0
  31. package/lib/src/x4.ts +4 -0
  32. package/lib/styles/x4.css +1 -1
  33. package/lib/types/x4js.d.ts +369 -234
  34. package/package.json +18 -28
  35. package/src/components/boxes/boxes.ts +101 -9
  36. package/src/components/canvas/canvas.ts +1 -1
  37. package/src/components/canvas/canvas_ex.ts +13 -6
  38. package/src/components/components.ts +2 -1
  39. package/src/components/dialog/dialog.module.scss +3 -4
  40. package/src/components/dialog/dialog.ts +19 -2
  41. package/src/components/form/form.ts +1 -1
  42. package/src/components/icon/icon.module.scss +1 -0
  43. package/src/components/icon/icon.ts +0 -2
  44. package/src/components/input/input.ts +46 -22
  45. package/src/components/keyboard/keyboard.ts +29 -28
  46. package/src/components/menu/menu.ts +2 -2
  47. package/src/components/popup/popup.module.scss +6 -0
  48. package/src/components/popup/popup.ts +5 -3
  49. package/src/components/propgrid/progrid.module.scss +4 -0
  50. package/src/components/propgrid/propgrid.ts +22 -5
  51. package/src/components/sizers/sizer.ts +2 -1
  52. package/src/components/tabs/tabs.ts +6 -0
  53. package/src/components/textedit/textedit.ts +26 -21
  54. package/src/core/component.ts +15 -21
  55. package/src/core/core_application.ts +2 -1
  56. package/src/core/core_data.ts +9 -8
  57. package/src/core/core_element.ts +5 -0
  58. package/src/core/core_i18n.ts +1 -1
  59. package/src/core/core_router.ts +2 -2
  60. package/src/core/core_state.ts +62 -0
  61. package/src/core/core_svg.ts +1 -0
  62. package/src/x4.ts +4 -0
  63. package/.vscode/launch.json +0 -14
  64. package/demo/assets/house-light.svg +0 -1
  65. package/demo/assets/radio.svg +0 -4
  66. package/demo/index.html +0 -12
  67. package/demo/main.scss +0 -23
  68. package/demo/main.ts +0 -324
  69. package/demo/package.json +0 -26
  70. package/demo/scss.d.ts +0 -4
  71. package/demo/svg.d.ts +0 -1
  72. package/demo/tsconfig.json +0 -14
  73. package/scripts/build.mjs +0 -378
  74. package/scripts/prepack.mjs +0 -346
  75. package/tsconfig.json +0 -14
@@ -120,30 +120,24 @@ export class Keyboard extends HBox<KeyboardProps>
120
120
 
121
121
  this.hide( );
122
122
 
123
- /*
124
- touchstart: (e) => {
125
- console.log( 'touch start' );
126
- //e.preventDefault( );
123
+ this.addDOMEvent( "mousedown", (e) => {
124
+ this.handleKey( e );
125
+ e.preventDefault( );
127
126
  e.stopPropagation( );
128
-
129
- this.last_ev = e;
130
- clearInterval( this.autorepeat );
131
- this.autorepeat = setInterval( () => {
132
- this.handleKey( this.last_ev );
133
- }, 250 );
134
- },
135
- touchend: ( e ) => {
136
- console.log( 'touch end' );
137
- clearInterval( this.autorepeat );
138
- this.last_ev = null;
139
- },
140
- */
127
+ });
141
128
 
142
- this.addDOMEvent( "mousedown", (e) => {
129
+ // for rapid people
130
+ this.addDOMEvent( "dblclick", (e) => {
143
131
  this.handleKey( e );
144
132
  e.preventDefault( );
145
133
  e.stopPropagation( );
146
134
  });
135
+
136
+ // for slow people
137
+ this.addDOMEvent( "contextmenu", (e) => {
138
+ e.preventDefault( );
139
+ e.stopPropagation( );
140
+ });
147
141
  }
148
142
 
149
143
  /**
@@ -285,20 +279,27 @@ export class Keyboard extends HBox<KeyboardProps>
285
279
 
286
280
  if( this.visible ) {
287
281
  if( this.input ) {
288
- this.show( );
289
- this._scrollIntoView( this.input );
290
-
291
282
  const type = this.input.type;
292
- const dtype = this.input.getAttribute( "data-type" );
293
283
 
294
- if( type==='number' || dtype==='number' ) {
295
- this._switchMode( "number" )
284
+ if( type=='check' || type=="radio" ) {
285
+ this.hide( );
286
+ this.input = null;
296
287
  }
297
- else if( type==='date' || dtype==='date' ) {
298
- this._switchMode( "date");
299
- }
300
288
  else {
301
- this._switchMode( "lower" );
289
+ this.show( );
290
+ this._scrollIntoView( this.input );
291
+
292
+ const dtype = this.input.getAttribute( "data-type" );
293
+
294
+ if( type==='number' || dtype==='number' ) {
295
+ this._switchMode( "number" )
296
+ }
297
+ else if( type==='date' || dtype==='date' ) {
298
+ this._switchMode( "date");
299
+ }
300
+ else {
301
+ this._switchMode( "lower" );
302
+ }
302
303
  }
303
304
  }
304
305
  }
@@ -38,7 +38,7 @@ export interface MenuItem {
38
38
  click?: DOMEventHandler;
39
39
  }
40
40
 
41
- type MenuElement = MenuItem | Component | string;
41
+ export type MenuElement = MenuItem | Component | string;
42
42
 
43
43
  export interface MenuProps extends Omit<PopupProps,"content"> {
44
44
  items: MenuElement[];
@@ -149,7 +149,7 @@ class CMenuItem extends Component {
149
149
  export class Menu extends Popup {
150
150
 
151
151
  constructor( props: MenuProps ) {
152
- super( { ...props, autoClose: "menu", modal: false } );
152
+ super( { ...props, autoClose: "menu" } );
153
153
 
154
154
  this.addClass( "x4vbox" );
155
155
 
@@ -32,6 +32,12 @@
32
32
  }
33
33
  }
34
34
 
35
+ .x4popup.center {
36
+ left: 50%;
37
+ top: 50%;
38
+ transform: translate(-50%, -50%);
39
+ }
40
+
35
41
  ::backdrop,
36
42
  .x4modal-mask {
37
43
  background-color: var( --modal-mask-background );
@@ -129,11 +129,13 @@ export class Popup<P extends PopupProps = PopupProps, E extends PopupEvents = Po
129
129
  }
130
130
 
131
131
  /**
132
- *
132
+ * s
133
133
  */
134
134
 
135
- displayCenter( ) {
136
- this.displayNear( new Rect( window.innerWidth/2, window.innerHeight/2, 0, 0 ), "center middle" );
135
+ displayCenter( center = true ) {
136
+ //this.displayNear( new Rect( window.innerWidth/2, window.innerHeight/2, 0, 0 ), "center middle" );
137
+ this.setClass( 'center', center );
138
+ this._do_show( ); // to compute size
137
139
  }
138
140
 
139
141
  /**
@@ -104,5 +104,9 @@
104
104
  padding: 8px;
105
105
  padding-left: 34px;
106
106
  }
107
+
108
+ .cell:has(.x4button) {
109
+ padding: 4px 40px;
110
+ }
107
111
  }
108
112
  }
@@ -20,7 +20,7 @@ import { Component, ComponentProps, EvChange, EvFocus } from "../../core/compone
20
20
  import { HBox, VBox } from "../boxes/boxes"
21
21
  import { Input, InputProps } from "../input/input"
22
22
  import { ListItem } from "../listbox/listbox"
23
- import { SimpleText } from "../label/label"
23
+ import { Label, SimpleText } from "../label/label"
24
24
  import { class_ns, isFunction } from '../../core/core_tools';
25
25
  import { Icon } from '../components.js'
26
26
 
@@ -32,7 +32,7 @@ type IValue = boolean | number | string;
32
32
  type IValueCB = ( name: string) => IValue;
33
33
 
34
34
  export interface PropertyValue {
35
- type: 'boolean' | 'number' | 'string' | 'password' | 'options';
35
+ type: 'boolean' | 'number' | 'string' | 'password' | 'options' | 'label' | 'button';
36
36
  title?: string;
37
37
  desc?: string;
38
38
  name?: string;
@@ -82,7 +82,7 @@ export class PropertyGrid extends VBox {
82
82
  setItems( _grps: PropertyGroup[] ) {
83
83
 
84
84
  this.groups = _grps;
85
- this.groups.sort( (a,b) => {return a.title>b.title ? 1 : 0} );
85
+ //this.groups.sort( (a,b) => {return a.title>b.title ? 1 : 0} );
86
86
 
87
87
  let items: Component[] = [];
88
88
 
@@ -145,6 +145,8 @@ export class PropertyGrid extends VBox {
145
145
 
146
146
  makePropertyRow( item: PropertyValue ) {
147
147
 
148
+ let use_hdr = true;
149
+
148
150
  // If boolean create checkbox
149
151
  let editor: Component;
150
152
  let value = isFunction(item.value) ? item.value( item.name ) : item.value;
@@ -171,7 +173,6 @@ export class PropertyGrid extends VBox {
171
173
  items: item.options,
172
174
  name: item.name,
173
175
  change: ( e: EvChange ) => {
174
- debugger;
175
176
  item.callback?.( item.name, e.value );
176
177
  }
177
178
  });
@@ -208,6 +209,22 @@ export class PropertyGrid extends VBox {
208
209
  }
209
210
  });
210
211
  }
212
+ else if (item.type === 'label') {
213
+ editor = new Label({
214
+ id: item.name,
215
+ text: value as string,
216
+ });
217
+ }
218
+ else if (item.type === 'button') {
219
+ use_hdr = false;
220
+ editor = new Button({
221
+ id: item.name,
222
+ label: value as string,
223
+ click: ( ) => {
224
+ item.callback?.( item.name, '' );
225
+ }
226
+ });
227
+ }
211
228
  else {
212
229
  editor = new Input({
213
230
  type: 'text',
@@ -225,7 +242,7 @@ export class PropertyGrid extends VBox {
225
242
  return new HBox({
226
243
  cls: 'row',
227
244
  content: [
228
- new Component({ cls: 'cell hdr', content: item.title ?? item.name, tooltip: item.desc }),
245
+ use_hdr ? new Component({ cls: 'cell hdr', content: item.title ?? item.name, tooltip: item.desc }) : null,
229
246
  new Component({ cls: 'cell', tag: "label", attrs: { "labelFor": item.name }, content: editor })
230
247
  ]
231
248
  });
@@ -33,12 +33,13 @@ interface CSizerEvent extends ComponentEvents {
33
33
  stop: ComponentEvent;
34
34
  }
35
35
 
36
- type SizerType = "left" | "top" | "right" | "bottom";
36
+ type SizerType = "left" | "top" | "right" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right";
37
37
 
38
38
  /**
39
39
  *
40
40
  */
41
41
 
42
+
42
43
  @class_ns( "x4" )
43
44
  export class CSizer extends Component<ComponentProps,CSizerEvent> {
44
45
 
@@ -138,6 +138,7 @@ export class Tabs extends VBox<TabsProps> {
138
138
 
139
139
  private _list: CTabList;
140
140
  private _stack: StackBox;
141
+ private _current: string;
141
142
 
142
143
  constructor( props: TabsProps ) {
143
144
  super( props );
@@ -169,6 +170,7 @@ export class Tabs extends VBox<TabsProps> {
169
170
  selectTab( name: string ) {
170
171
  this._list.select( name );
171
172
  this._stack.select( name );
173
+ this._current = name;
172
174
  }
173
175
 
174
176
  private _onclick( ev: TablistClickEvent ) {
@@ -183,6 +185,10 @@ export class Tabs extends VBox<TabsProps> {
183
185
  return this._stack.getPage( name );
184
186
  }
185
187
 
188
+ getCurTab( ) {
189
+ return this._current;
190
+ }
191
+
186
192
  /**
187
193
  *
188
194
  */
@@ -14,12 +14,12 @@
14
14
  * that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
15
15
  **/
16
16
 
17
- import { EventCallback } from '@core/core_events.js';
18
- import { Component, ComponentProps, EvChange, EvClick, EvFocus, makeUniqueComponentId } from '../../core/component';
17
+ //import { EventCallback } from '@core/core_events.js';
18
+ import { Component, makeUniqueComponentId } from '../../core/component';
19
19
  import { class_ns, UnsafeHtml } from '../../core/core_tools';
20
20
 
21
21
  import { HBox } from '../boxes/boxes';
22
- import { Input, TextInputProps } from "../input/input"
22
+ import { DateProps, Input, InputProps, NumberProps, TextInputProps, TimeProps } from "../input/input"
23
23
  import { Label } from '../label/label';
24
24
 
25
25
  import "./textedit.module.scss";
@@ -30,12 +30,28 @@ import "./textedit.module.scss";
30
30
  *
31
31
  */
32
32
 
33
- interface TextEditProps extends ComponentProps {
33
+ type TextEditInputs = TextInputProps | NumberProps | DateProps | TimeProps;
34
+
35
+ interface TextEditBase {
36
+ label: string | UnsafeHtml;
37
+ labelWidth?: number;
38
+ inputWidth?: number;
39
+ inputId?: string;
40
+ inputGadgets?: Component[];
41
+ inputAttrs?: any;
42
+ }
43
+
44
+ type TextEditProps = TextEditInputs & TextEditBase;
45
+
46
+ /*
47
+ not enougth precise
48
+ interface TextEditProps extends InputProps {
34
49
  label: string | UnsafeHtml;
35
50
  labelWidth?: number;
51
+ editWidth?: number;
36
52
  inputId?: string;
37
53
 
38
- type?: "text" | "email" | "password" | "date" | "number";
54
+ type?: "text" | "email" | "password" | "date" | "number" | "time";
39
55
  name?: string;
40
56
  readonly?: boolean;
41
57
  required?: boolean;
@@ -49,6 +65,7 @@ interface TextEditProps extends ComponentProps {
49
65
  focus?: EventCallback<EvFocus>;
50
66
  change?: EventCallback<EvChange>;
51
67
  }
68
+ */
52
69
 
53
70
  /**
54
71
  *
@@ -71,26 +88,14 @@ export class TextEdit extends HBox {
71
88
  }
72
89
 
73
90
  const gadgets = props.inputGadgets ?? [];
91
+ const iprops: InputProps = { ...props, id: props.inputId, attrs: props.inputAttrs, width: props.inputWidth };
74
92
 
75
93
  this.setContent( [
76
- new HBox( { id: "label", width: props.labelWidth, content: [
94
+ props.label ? new HBox( { id: "label", width: props.labelWidth, content: [
77
95
  new Label( { tag: "label", text: props.label, labelFor: props.inputId } ),
78
- ]}),
96
+ ]}) : null,
79
97
  new HBox( { id: "edit", content: [
80
- this.input = new Input( {
81
- type: props.type ?? "text",
82
- readonly: props.readonly,
83
- value: props.value,
84
- name: props.name,
85
- id: props.inputId,
86
- required: props.required,
87
- disabled: props.disabled,
88
- placeholder: props.placeholder,
89
- autofocus: props.autofocus,
90
- attrs: props.inputAttrs,
91
- focus: props.focus,
92
- change: props.change,
93
- } ),
98
+ this.input = new Input( iprops ),
94
99
  ...gadgets,
95
100
  ]})
96
101
  ])
@@ -25,6 +25,7 @@ interface RefType<T extends Component> {
25
25
  }
26
26
 
27
27
  type ComponentAttributes = Record<string,string|number|boolean>;
28
+ type CreateComponentCallBack = ( attrs: Record<string,string> ) => ComponentContent;
28
29
 
29
30
  const FRAGMENT = Symbol( "fragment" );
30
31
  const COMPONENT = Symbol( "component" );
@@ -43,7 +44,7 @@ const RE_NUMBER = /^-?\d+(\.\d*)?$/;
43
44
 
44
45
  function genClassNames( x: any ): string[] {
45
46
 
46
- let classes = [];
47
+ const classes = [];
47
48
  let self = Object.getPrototypeOf(x);
48
49
 
49
50
  if( self.constructor==Component ) {
@@ -52,7 +53,7 @@ function genClassNames( x: any ): string[] {
52
53
 
53
54
  while (self && self.constructor !== Component ) {
54
55
  const clsname:string = self.constructor.name;
55
- const clsns: string = self.constructor.hasOwnProperty(x4_class_ns_sym) ? self.constructor[x4_class_ns_sym] : "";
56
+ const clsns: string = Object.prototype.hasOwnProperty.call(self.constructor,x4_class_ns_sym) ? self.constructor[x4_class_ns_sym] : "";
56
57
  classes.push( clsns+clsname.toLowerCase() );
57
58
  self = Object.getPrototypeOf(self);
58
59
  }
@@ -134,7 +135,7 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
134
135
  readonly props: P;
135
136
  protected readonly clsprefix: string; // internal class name prefix (x4 internal)
136
137
 
137
- #store: Map<string|Symbol,any>;
138
+ #store: Map<string|symbol,any>;
138
139
 
139
140
  constructor( props: P ) {
140
141
  super( );
@@ -303,9 +304,10 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
303
304
  *
304
305
  */
305
306
 
306
- setClass( cls: string, set: boolean = true ) {
307
+ setClass( cls: string, set: boolean = true ) : this {
307
308
  if( set ) this.addClass(cls);
308
309
  else this.removeClass( cls );
310
+ return this;
309
311
  }
310
312
 
311
313
  // :: ATTRIBUTES ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@@ -314,10 +316,11 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
314
316
  * attributes
315
317
  */
316
318
 
317
- setAttributes( attrs: ComponentAttributes ) {
319
+ setAttributes( attrs: ComponentAttributes ): this {
318
320
  for( const name in attrs ) {
319
321
  this.setAttribute( name, attrs[name] );
320
322
  }
323
+ return this;
321
324
  }
322
325
 
323
326
  /**
@@ -373,7 +376,7 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
373
376
  * idem as setData but onot on dom, you can store anything
374
377
  */
375
378
 
376
- setInternalData( name: string|Symbol, value: any ): this {
379
+ setInternalData( name: string|symbol, value: any ): this {
377
380
  if( !this.#store ) {
378
381
  this.#store = new Map( );
379
382
  }
@@ -382,7 +385,7 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
382
385
  return this;
383
386
  }
384
387
 
385
- getInternalData( name: string|Symbol ): any {
388
+ getInternalData( name: string|symbol ): any {
386
389
  return this.#store?.get(name);
387
390
  }
388
391
 
@@ -417,7 +420,7 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
417
420
  protected mapPropEvents<N extends keyof E>(props: P, ...elements: N[] ) {
418
421
  const p = props as any;
419
422
  elements.forEach( n => {
420
- if (p.hasOwnProperty(n) && p[n]) {
423
+ if (Object.prototype.hasOwnProperty.call(p,n) && p[n]) {
421
424
  this.on( n, p[n] );
422
425
  }
423
426
  });
@@ -675,7 +678,7 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
675
678
  *
676
679
  */
677
680
 
678
- getBoundingRect( ): IRect {
681
+ getBoundingRect( ): Rect {
679
682
  const rc = this.dom.getBoundingClientRect( );
680
683
  return new Rect( rc.x, rc.y, rc.width, rc.height );
681
684
  }
@@ -843,7 +846,7 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
843
846
 
844
847
  enumChildComponents( recursive: boolean ) {
845
848
 
846
- let children: Component[] = [];
849
+ const children: Component[] = [];
847
850
 
848
851
  const nodes = this.enumChildNodes( recursive );
849
852
  nodes.forEach( ( c: Node ) => {
@@ -861,7 +864,7 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
861
864
  */
862
865
 
863
866
  enumChildNodes( recursive: boolean ) {
864
- let children: Node[] = Array.from( recursive ? this.dom.querySelectorAll( '*' ) : this.dom.children );
867
+ const children: Node[] = Array.from( recursive ? this.dom.querySelectorAll( '*' ) : this.dom.children );
865
868
  return children;
866
869
  }
867
870
 
@@ -880,7 +883,7 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
880
883
  * called by the compiler when a jsx element is seen
881
884
  */
882
885
 
883
- static createElement( clsOrTag: string | ComponentConstructor | Symbol | Function, attrs: any, ...children: Component[] ): Component | Component[] {
886
+ static createElement( clsOrTag: string | ComponentConstructor | symbol | CreateComponentCallBack, attrs: any, ...children: Component[] ): Component | Component[] {
884
887
 
885
888
  let comp: Component;
886
889
 
@@ -1036,15 +1039,6 @@ export interface EvContextMenu extends ComponentEvent {
1036
1039
  uievent: UIEvent; // UI event that fire this event
1037
1040
  }
1038
1041
 
1039
- /**
1040
- * Simple message
1041
- */
1042
-
1043
- export interface EvMessage extends ComponentEvent {
1044
- readonly msg: string;
1045
- readonly params?: any;
1046
- }
1047
-
1048
1042
  /**
1049
1043
  * Drag/Drop event
1050
1044
  */
@@ -62,6 +62,7 @@ export class Application<E extends ApplicationEvents = ApplicationEvents> extend
62
62
  super( );
63
63
 
64
64
  console.assert( main_app==null, "Application must be a singleton." );
65
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
65
66
  main_app = this;
66
67
  }
67
68
 
@@ -185,7 +186,7 @@ export class Application<E extends ApplicationEvents = ApplicationEvents> extend
185
186
  // then we send them on websocket
186
187
 
187
188
  this.on( 'global', ( e: EvMessage ) => {
188
- if( e.hasOwnProperty(socket_sent) ) {
189
+ if( Object.prototype.hasOwnProperty.call( e, socket_sent) ) {
189
190
  return;
190
191
  }
191
192
 
@@ -77,7 +77,7 @@ const metaFields = Symbol( 'metaField' );
77
77
  function _getMetas( obj: object, create = true ) : MetaInfos {
78
78
 
79
79
  let ctor = obj.constructor as any;
80
- let mfld = ctor.hasOwnProperty(metaFields) ? ctor[metaFields] : undefined;
80
+ let mfld = Object.prototype.hasOwnProperty.call(ctor,metaFields) ? ctor[metaFields] : undefined;
81
81
 
82
82
  if( mfld===undefined ) {
83
83
  if( !create ) {
@@ -105,6 +105,7 @@ function _getMetas( obj: object, create = true ) : MetaInfos {
105
105
  return mfld;
106
106
  }
107
107
 
108
+ // eslint-disable-next-line @typescript-eslint/no-namespace
108
109
  export namespace data {
109
110
 
110
111
  /**
@@ -297,7 +298,7 @@ export class DataModel {
297
298
  * @returns an object with known record values
298
299
  */
299
300
 
300
- serialize( input: DataRecord ): any {
301
+ serialize<T = any>( input: DataRecord ): T {
301
302
  let rec: any = {};
302
303
 
303
304
  this.getFields().forEach((f) => {
@@ -306,8 +307,10 @@ export class DataModel {
306
307
  }
307
308
  });
308
309
 
309
- return rec;
310
+ return rec as T;
310
311
  }
312
+
313
+
311
314
 
312
315
  /**
313
316
  * default unserializer
@@ -454,11 +457,7 @@ export class DataModel {
454
457
  *
455
458
  */
456
459
 
457
-
458
-
459
- const $model = Symbol( "model" )
460
-
461
- export class DataRecord {
460
+ export class DataRecord {
462
461
  [ key: string ]: DataFieldValue;
463
462
 
464
463
  /*
@@ -511,6 +510,8 @@ export class DataRecord {
511
510
  this.setRaw( fld.name, value );
512
511
  }
513
512
  */
513
+
514
+
514
515
  }
515
516
 
516
517
 
@@ -84,6 +84,11 @@ export class CoreElement<E extends EventMap = EventMap> {
84
84
  }
85
85
 
86
86
  this.#events.addListener( name, listener );
87
+ return {
88
+ off: ( ) => {
89
+ this.#events.removeListener( name, listener );
90
+ }
91
+ }
87
92
  }
88
93
 
89
94
  /**
@@ -97,7 +97,7 @@ export function addTranslation( name: string, ...parts: any[] ) {
97
97
 
98
98
  function _patch( obj: any, by: any ) {
99
99
 
100
- for( let n in by ) {
100
+ for( const n in by ) {
101
101
  const src = by[n];
102
102
  if( typeof src === "string" ) {
103
103
  obj[n] = src;
@@ -60,7 +60,7 @@ export function parseRoute(str: string | RegExp, loose = false): Segment {
60
60
  const ext = tmp.indexOf('.', 1);
61
61
 
62
62
  keys.push(tmp.substring(1, o >= 0 ? o : ext >= 0 ? ext : tmp.length));
63
- pattern += o >= 0 && ext < 0 ? '(?:/([^\/]+?))?' : '/([^\/]+?)';
63
+ pattern += o >= 0 && ext < 0 ? '(?:/([^/]+?))?' : '/([^/]+?)';
64
64
  if (ext >= 0) {
65
65
  pattern += (o >= 0 ? '?' : '') + '\\' + tmp.substring(ext);
66
66
  }
@@ -72,7 +72,7 @@ export function parseRoute(str: string | RegExp, loose = false): Segment {
72
72
 
73
73
  return {
74
74
  keys,
75
- pattern: new RegExp( `^${pattern}${loose ? '(?=$|\/)' : '\/?$'}`, 'i' )
75
+ pattern: new RegExp( `^${pattern}${loose ? '(?=$|/)' : '/?$'}`, 'i' )
76
76
  };
77
77
  }
78
78
 
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @file core_state.ts
3
+ * @author Etienne Cochard
4
+ * @copyright (c) 2025 R-libre ingenierie, all rights reserved.
5
+ **/
6
+
7
+
8
+ type StateData = boolean | number | string | Date | any;
9
+ type State = Record<string,StateData>;
10
+
11
+ export class StateManager {
12
+
13
+ private _state: StateData;
14
+ private _subscribers: Map<string,any>;
15
+ private _currentTracking: Set<string>;
16
+
17
+ constructor(initialState: StateData ) {
18
+ this._state = initialState ? { ...initialState } : {};
19
+ this._subscribers = new Map();
20
+ this._currentTracking = new Set( );
21
+ }
22
+
23
+ getState( path: string, defaultValue: StateData = null) {
24
+ // Optional tracking for reactivity
25
+ if (this._currentTracking) {
26
+ this._currentTracking.add(path);
27
+ }
28
+ // Fast path-based access
29
+ const parts = path.split('.');
30
+ let current = this._state;
31
+
32
+ for (const part of parts) {
33
+ if (current?.[part] === undefined) {
34
+ return defaultValue;
35
+ }
36
+
37
+ current = current[part];
38
+ }
39
+
40
+ return current;
41
+ }
42
+
43
+ setState(path: string, value: StateData, context: any = {} ) {
44
+ // Update state
45
+ const parts = path.split('.');
46
+ let current = this._state;
47
+
48
+ for (let i = 0; i < parts.length - 1; i++) {
49
+ const part = parts[i];
50
+ if (!current[part] || typeof current[part] !== 'object') {
51
+ current[part] = {};
52
+ }
53
+
54
+ current = current[part];
55
+ }
56
+
57
+ current[parts[parts.length - 1]] = value;
58
+
59
+ // Notify subscribers
60
+ this._notifySubscribers(path, value);
61
+ }
62
+ }
@@ -198,6 +198,7 @@ class SvgItem {
198
198
  addClass( cls: string ): this {
199
199
  if( !cls ) return;
200
200
 
201
+ cls = cls.trim();
201
202
  if( cls.indexOf(' ')>=0 ) {
202
203
  const ccs = cls.split( " " );
203
204
  this._dom.classList.add(...ccs);
package/src/x4.ts CHANGED
@@ -15,13 +15,17 @@
15
15
  **/
16
16
 
17
17
  export * from "./core/component"
18
+ export * from "./core/core_application"
18
19
  export * from "./core/core_colors"
20
+ export * from "./core/core_data"
19
21
  export * from "./core/core_dom"
20
22
  export * from "./core/core_dragdrop"
21
23
  export * from "./core/core_element"
22
24
  export * from "./core/core_events"
23
25
  export * from "./core/core_i18n"
26
+ export * from "./core/core_react"
24
27
  export * from "./core/core_router"
28
+ export * from "./core/core_state"
25
29
  export * from "./core/core_styles"
26
30
  export * from "./core/core_svg"
27
31
  export * from "./core/core_tools"