x4js 2.0.28 → 2.0.31

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 (69) hide show
  1. package/.vscode/launch.json +14 -0
  2. package/.vscode/settings.json +2 -0
  3. package/ai-comments.txt +97 -0
  4. package/demo/assets/house-light.svg +1 -0
  5. package/demo/assets/radio.svg +4 -0
  6. package/demo/index.html +12 -0
  7. package/demo/main.scss +23 -0
  8. package/demo/main.ts +324 -0
  9. package/demo/package.json +26 -0
  10. package/demo/scss.d.ts +4 -0
  11. package/demo/svg.d.ts +1 -0
  12. package/demo/tsconfig.json +14 -0
  13. package/lib/types/x4js.d.ts +0 -2374
  14. package/package.json +23 -47
  15. package/src/colors.scss +246 -0
  16. package/src/components/boxes/boxes.module.scss +1 -1
  17. package/src/components/boxes/boxes.ts +139 -28
  18. package/src/components/button/button.ts +76 -29
  19. package/src/components/combobox/combobox.ts +1 -1
  20. package/src/components/dialog/dialog.ts +4 -0
  21. package/src/components/gridview/gridview.ts +104 -6
  22. package/src/components/icon/icon.ts +42 -14
  23. package/src/components/input/input.ts +146 -74
  24. package/src/components/keyboard/keyboard.module.scss +1 -1
  25. package/src/components/keyboard/keyboard.ts +31 -9
  26. package/src/components/label/label.module.scss +9 -0
  27. package/src/components/label/label.ts +10 -6
  28. package/src/components/link/link.module.scss +44 -0
  29. package/src/components/link/link.ts +7 -1
  30. package/src/components/listbox/listbox.module.scss +18 -4
  31. package/src/components/listbox/listbox.ts +32 -12
  32. package/src/components/menu/menu.module.scss +14 -2
  33. package/src/components/menu/menu.ts +1 -1
  34. package/src/components/messages/messages.ts +13 -5
  35. package/src/components/panel/panel.module.scss +7 -0
  36. package/src/components/popup/popup.ts +14 -10
  37. package/src/components/propgrid/propgrid.ts +1 -1
  38. package/src/components/shared.scss +4 -0
  39. package/src/components/spreadsheet/spreadsheet.ts +81 -34
  40. package/src/components/tabs/tabs.module.scss +1 -0
  41. package/src/components/textarea/textarea.ts +8 -2
  42. package/src/components/textedit/textedit.ts +7 -0
  43. package/src/components/themes.scss +2 -0
  44. package/src/components/tooltips/tooltips.ts +15 -3
  45. package/src/core/component.ts +358 -162
  46. package/src/core/core_application.ts +129 -32
  47. package/src/core/core_colors.ts +382 -119
  48. package/src/core/core_data.ts +73 -86
  49. package/src/core/core_dom.ts +10 -0
  50. package/src/core/core_dragdrop.ts +32 -7
  51. package/src/core/core_element.ts +111 -4
  52. package/src/core/core_events.ts +48 -11
  53. package/src/core/core_i18n.ts +2 -0
  54. package/src/core/core_pdf.ts +454 -0
  55. package/src/core/core_router.ts +64 -5
  56. package/src/core/core_state.ts +1 -0
  57. package/src/core/core_styles.ts +11 -12
  58. package/src/core/core_svg.ts +346 -51
  59. package/src/core/core_tools.ts +105 -17
  60. package/src/x4.d.ts +10 -0
  61. package/src/x4.ts +1 -0
  62. package/src/x4tsx.d.ts +2 -1
  63. package/tsconfig.json +11 -0
  64. package/lib/README.txt +0 -20
  65. package/lib/cjs/x4.css +0 -1
  66. package/lib/cjs/x4.js +0 -2
  67. package/lib/esm/x4.css +0 -1
  68. package/lib/esm/x4.mjs +0 -2
  69. package/lib/styles/x4.css +0 -1
@@ -56,6 +56,8 @@ export interface ListboxEvents extends ComponentEvents {
56
56
  export interface ListboxProps extends Omit<ComponentProps,'content'> {
57
57
  items?: ListItem[];
58
58
  renderer?: ( item: ListItem ) => Component;
59
+ title?: string;
60
+ icon?: string;
59
61
  header?: Header;
60
62
  footer?: Component,
61
63
  checkable?: true,
@@ -104,6 +106,7 @@ export class Listbox extends Component<ListboxProps,ListboxEvents> {
104
106
  }
105
107
 
106
108
  this.setContent( [
109
+ (props.title || props.icon) ? new Label( { cls: 'title', text: props.title, icon: props.icon }) : null,
107
110
  props.header ? props.header : null,
108
111
  scroller,
109
112
  props.footer,
@@ -117,7 +120,7 @@ export class Listbox extends Component<ListboxProps,ListboxEvents> {
117
120
  } );
118
121
 
119
122
  if( props.items ) {
120
- this.setItems( props.items );
123
+ this.setItems( props.items, false );
121
124
  }
122
125
  }
123
126
 
@@ -191,8 +194,15 @@ export class Listbox extends Component<ListboxProps,ListboxEvents> {
191
194
  }
192
195
  else {
193
196
  const selitem = this._itemWithID( this._lastsel );
194
- let nel = sens==kbNav.next ? selitem.nextElement() : selitem.prevElement();
195
- nel = next_visible( nel, sens==kbNav.next );
197
+
198
+ let nel;
199
+ if( selitem ) {
200
+ nel = sens==kbNav.next ? selitem.nextElement() : selitem.prevElement();
201
+ nel = next_visible( nel, sens==kbNav.next );
202
+ }
203
+ else {
204
+ nel = sens==kbNav.next ? this._view.firstChild() : this._view.lastChild( );
205
+ }
196
206
 
197
207
  if( nel ) {
198
208
  const id = nel.getInternalData( "id" );
@@ -406,10 +416,12 @@ export class Listbox extends Component<ListboxProps,ListboxEvents> {
406
416
  this._multisel.clear( );
407
417
  }
408
418
 
409
- clearSelection( ) {
419
+ clearSelection( fireEvent = true ) {
410
420
  if( this._multisel.size ) {
411
421
  this._clearSelection( );
412
- this.fire( "selectionChange", { selection: [], empty: true } );
422
+ if( fireEvent ) {
423
+ this.fire( "selectionChange", { selection: [], empty: true } );
424
+ }
413
425
  }
414
426
  }
415
427
 
@@ -425,24 +437,21 @@ export class Listbox extends Component<ListboxProps,ListboxEvents> {
425
437
  this._view.clearContent( );
426
438
  this._items = items ?? [];
427
439
 
428
- let upsel = false;
440
+ let update_sel = false;
429
441
 
430
442
  if( this._items.length ) {
431
443
  const content = items.map( x => this.renderItem(x) );
432
444
  this._view.setContent( content );
433
445
 
434
- if( keepSel ) {
446
+ if( keepSel && oldSel.length>0 ) {
435
447
  this.select( oldSel );
436
448
  }
437
- else {
438
- upsel = true;
439
- }
440
449
  }
441
450
  else {
442
- upsel = true;
451
+ update_sel = oldSel.length>0;
443
452
  }
444
453
 
445
- if( upsel ) {
454
+ if( update_sel ) {
446
455
  this.setTimeout( "sel", 100, ( ) => {
447
456
  this.fire( "selectionChange", { selection: [], empty: true } );
448
457
  } );
@@ -573,4 +582,15 @@ export class Listbox extends Component<ListboxProps,ListboxEvents> {
573
582
  getSelection( ) {
574
583
  return Array.from( this._multisel );
575
584
  }
585
+
586
+ ensureSelectionVisible( ) {
587
+ const sels = Array.from( this._multisel.values() );
588
+ if( sels.length) {
589
+ const item = this._itemWithID( sels[0] );
590
+ item?.scrollIntoView( {
591
+ behavior: "instant",
592
+ block: "nearest"
593
+ } )
594
+ }
595
+ }
576
596
  }
@@ -17,7 +17,7 @@
17
17
  @use "../shared.scss";
18
18
 
19
19
  :root {
20
- --menu-background: var( --background-primary );
20
+ --menu-background: var( --background-ternary );
21
21
  --menu-border: var( --border-hover );
22
22
 
23
23
  --menuitem-color: var( --text-ternary );;
@@ -28,7 +28,7 @@
28
28
  }
29
29
 
30
30
  .x4menu {
31
- @extend .shadow-lg;
31
+ @extend .shadow-xxl;
32
32
 
33
33
  position: absolute;
34
34
  overflow-y: auto;
@@ -39,6 +39,7 @@
39
39
 
40
40
  background-color: var(--menu-background);
41
41
  border: 1px solid var(--menu-border);
42
+ border-left: 4px solid var( --accent-background );
42
43
 
43
44
  max-height: calc( 100vh - 32px );
44
45
 
@@ -69,6 +70,17 @@
69
70
  }
70
71
  }
71
72
 
73
+ &.danger #icon {
74
+ .fa-primary {
75
+ fill: var( --color-danger-a50 );
76
+ }
77
+
78
+ .fa-secondary {
79
+ fill: var( --color-danger-a30 );
80
+ opacity: 1;
81
+ }
82
+ }
83
+
72
84
  #text {
73
85
  @extend .flex;
74
86
  }
@@ -34,7 +34,7 @@ export interface MenuItem {
34
34
  icon?: string;
35
35
  text: string | UnsafeHtml;
36
36
  menu?: Menu;
37
- disabled?: true;
37
+ disabled?: boolean;
38
38
  click?: DOMEventHandler;
39
39
  }
40
40
 
@@ -98,6 +98,11 @@ export class MessageBox extends Dialog<DialogProps>
98
98
  }
99
99
 
100
100
 
101
+ interface InputOptions {
102
+ password?: boolean;
103
+ trim?: boolean;
104
+ }
105
+
101
106
  @class_ns( "x4" )
102
107
  export class InputBox extends Dialog<DialogProps>
103
108
  {
@@ -110,7 +115,10 @@ export class InputBox extends Dialog<DialogProps>
110
115
  return input.getValue( );
111
116
  }
112
117
 
113
- private static _create( msg: string | UnsafeHtml, value: string, title: string ) {
118
+ private static _create( msg: string | UnsafeHtml, value: string, title: string, options?: InputOptions ) {
119
+
120
+ options = {trim: true, password: false, ...options };
121
+
114
122
  const box = new InputBox({
115
123
  modal: true,
116
124
  title,
@@ -122,7 +130,7 @@ export class InputBox extends Dialog<DialogProps>
122
130
  new Icon( { iconId: pen_icon }),
123
131
  new VBox( { flex: 1, content: [
124
132
  new Label( { text: msg } ),
125
- new Input( { value, type: "text" } )
133
+ new Input( { value, type: options.password ? "password" : "text", trim: options.trim } )
126
134
  ]})
127
135
  ]
128
136
  }),
@@ -138,11 +146,11 @@ export class InputBox extends Dialog<DialogProps>
138
146
  * idem with promise
139
147
  */
140
148
 
141
- static async showAsync( msg: string | UnsafeHtml, value: string, title?: string ) : Promise<string> {
149
+ static async showAsync( msg: string | UnsafeHtml, value: string, title?: string, options?: InputOptions ) : Promise<string> {
142
150
 
143
- return new Promise( (resolve, reject ) => {
151
+ return new Promise( (resolve, _reject ) => {
144
152
 
145
- const box = this._create( msg, value, title );
153
+ const box = this._create( msg, value, title, options );
146
154
 
147
155
  box.on( "btnclick", ( ev ) => {
148
156
  asap( ( ) => {
@@ -51,10 +51,17 @@
51
51
  border: none;
52
52
  border-top: 1px solid var( --border );
53
53
  margin-top: 1.5em;
54
+ border-radius: 0;
55
+
56
+ padding: 0;
54
57
 
55
58
  legend {
56
59
  background: none;
57
60
  top: -1.4em;
58
61
  left: 0;
59
62
  }
63
+
64
+ & >.body {
65
+ padding: 0;
66
+ }
60
67
  }
@@ -21,6 +21,7 @@ import { Rect, Point, class_ns, asap } from '../../core/core_tools';
21
21
  import { Box } from '../boxes/boxes'
22
22
 
23
23
  import "./popup.module.scss"
24
+ import { getGlobalZoom, getScrollbarSize } from '../../core/core_tools.js';
24
25
 
25
26
  export interface PopupEvents extends ComponentEvents {
26
27
  closed: ComponentEvent;
@@ -143,24 +144,27 @@ export class Popup<P extends PopupProps = PopupProps, E extends PopupEvents = Po
143
144
  */
144
145
 
145
146
  displayAt( x: number, y: number ) {
147
+ const zm = getGlobalZoom( );
148
+
146
149
  //TODO: check is already visible
147
150
  this.setStyle( {
148
- left: x+"px",
149
- top: y+"px",
151
+ left: (x/zm)+"px",
152
+ top: (y/zm)+"px",
150
153
  })
151
154
 
152
155
  this._do_show( ); // to compute size
153
156
 
154
- const rc = this.getBoundingRect( );
155
- const width = window.innerWidth - 16;
156
- const height = window.innerHeight - 16;
157
-
158
- if( rc.right>width ) {
159
- this.setStyleValue( "left", width-rc.width );
157
+ const rc = this.getBoundingRect( ).scale( 1/zm );
158
+ const sbw = getScrollbarSize( );
159
+
160
+ const screen_width = window.innerWidth - sbw;
161
+ if( rc.right>screen_width ) {
162
+ this.setStyleValue( "left", screen_width-rc.width );
160
163
  }
161
164
 
162
- if( rc.bottom>height ) {
163
- this.setStyleValue( "top", height-rc.height );
165
+ const screen_height = window.innerHeight - sbw;
166
+ if( rc.bottom>screen_height ) {
167
+ this.setStyleValue( "top", screen_height-rc.height );
164
168
  }
165
169
  }
166
170
 
@@ -73,7 +73,7 @@ export class PropertyGrid extends VBox {
73
73
  if( props.groups ) {
74
74
  this.setItems( props.groups );
75
75
  }
76
- };
76
+ }
77
77
 
78
78
  /**
79
79
  *
@@ -75,6 +75,10 @@
75
75
  box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
76
76
  }
77
77
 
78
+ .shadow-xxl {
79
+ box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.3), 0 8px 10px -6px rgb(0 0 0 / 0.3);
80
+ }
81
+
78
82
 
79
83
  @keyframes rotating {
80
84
  from {
@@ -18,7 +18,7 @@
18
18
  import { Component, ComponentContent, ComponentEvents, ComponentProps, EvClick, EvContextMenu, EvDblClick, EvSelectionChange, componentFromDOM } from '../../core/component';
19
19
  import { GridColumn } from '../gridview/gridview'
20
20
 
21
- import { class_ns, isNumber, isString, setWaitCursor } from '../../core/core_tools';
21
+ import { class_ns, isNumber, isString, UnsafeHtml } from '../../core/core_tools';
22
22
  import { CoreEvent, EventCallback, EventMap } from '../../core/core_events';
23
23
  import { kbNav } from '../../core/core_tools';
24
24
 
@@ -31,15 +31,21 @@ import { SimpleText } from '../label/label';
31
31
 
32
32
  import check_icon from "../checkbox/check.svg";
33
33
  import "./spreadsheet.module.scss"
34
- import { CoreElement, EvViewChange } from '../../x4.js';
34
+ import { CoreElement } from '../../x4.js';
35
35
 
36
36
  interface CellRef {
37
37
  col: number;
38
38
  row: number;
39
39
  }
40
40
 
41
+ export type CellClassifier = ( row: number, col: number ) => string; // return the cell computed class
42
+ export type RowClassifier = (row: number ) => string; // return the row computed class
41
43
  export type CellRenderer = (row: number, col: number, content: any) => Component;
42
44
 
45
+ export interface SpreadsheetColumn extends Omit<GridColumn,"classifier"> {
46
+ cellClassifier?: CellClassifier;
47
+ }
48
+
43
49
 
44
50
  function mkid(row: number, col: number) {
45
51
  return ((row & 0xfffff) << 12) | (col & 0xfff);
@@ -85,6 +91,7 @@ export class Store extends CoreElement<StoreEvents> {
85
91
  n.set(k, v);
86
92
  }
87
93
  });
94
+ this._data = n;
88
95
  }
89
96
 
90
97
  this._maxrows = rows;
@@ -108,6 +115,10 @@ export class Store extends CoreElement<StoreEvents> {
108
115
  return this._data.get(mkid(row, col));
109
116
  }
110
117
 
118
+ hasData( row: number, col?: number ) {
119
+ return this._data.has( col===undefined ? row : mkid(row, col));
120
+ }
121
+
111
122
  lock() {
112
123
  this._lock++;
113
124
  }
@@ -121,6 +132,29 @@ export class Store extends CoreElement<StoreEvents> {
121
132
  }
122
133
  }
123
134
 
135
+ removeRow( row_num: number ) {
136
+
137
+ if( row_num>=this._maxrows ) {
138
+ return;
139
+ }
140
+
141
+ const n = new Map<number, any>();
142
+ this._data.forEach( (v, k) => {
143
+ const row = k >> 12;
144
+ if (row != row_num) {
145
+ if( row>row_num ) {
146
+ k = mkid(row-1,k&0xfff)
147
+ }
148
+
149
+ n.set(k, v);
150
+ }
151
+ } );
152
+ this._data = n;
153
+
154
+ this._maxrows--;
155
+ this._changed( );
156
+ }
157
+
124
158
  private _changed() {
125
159
  if (!this._lock) {
126
160
  this.fire("changed", {});
@@ -130,6 +164,12 @@ export class Store extends CoreElement<StoreEvents> {
130
164
  this._change = true;
131
165
  }
132
166
  }
167
+
168
+ clear( ) {
169
+ this._data = new Map();
170
+ this._maxrows = 0;
171
+ this._changed( );
172
+ }
133
173
  }
134
174
 
135
175
 
@@ -149,7 +189,9 @@ export interface SpreadsheetEvents extends ComponentEvents {
149
189
  export interface SpreadsheetProps extends ComponentProps {
150
190
  footer?: boolean;
151
191
  store: Store;
152
- columns: GridColumn[];
192
+ columns: SpreadsheetColumn[];
193
+ rowClassifier?: RowClassifier;
194
+
153
195
 
154
196
  click?: EventCallback<EvClick>;
155
197
  dblClick?: EventCallback<EvDblClick>;
@@ -169,7 +211,7 @@ export interface SpreadsheetProps extends ComponentProps {
169
211
  @class_ns("x4")
170
212
  export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extends SpreadsheetEvents = SpreadsheetEvents> extends Component<P, E> {
171
213
 
172
- private _columns: GridColumn[];
214
+ private _columns: SpreadsheetColumn[];
173
215
  private _store: Store;
174
216
 
175
217
  private _lock: number;
@@ -427,9 +469,17 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
427
469
  return;
428
470
  }
429
471
 
430
- //if (ev.change_type == 'change') {
431
- this._selection.clear();
432
- //}
472
+ // try to keep selection
473
+ if (ev.type == 'changed' && this._selection.size ) {
474
+ const nsel = new Set<number>();
475
+ this._selection.forEach(x => {
476
+ if( this._store.hasData( x ) ) {
477
+ nsel.add( x );
478
+ }
479
+ });
480
+
481
+ this._selection = nsel;
482
+ }
433
483
 
434
484
  this._updateFlexs();
435
485
  this._computeFullSize();
@@ -598,10 +648,10 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
598
648
  return header;
599
649
  }
600
650
  /**
601
- *
651
+ * extra_cls est input/output
602
652
  */
603
653
 
604
- private _renderCell(row: number, column: GridColumn, extra_cls: string[]): ComponentContent {
654
+ private _renderCell(row: number, column: SpreadsheetColumn, extra_cls: string[]): ComponentContent {
605
655
 
606
656
  const col = column.id;
607
657
  const type = column.type;
@@ -611,14 +661,14 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
611
661
  return null;
612
662
  }
613
663
 
614
- let cls = "";
615
- //if( column.classifier ) {
616
- // extra_cls.push( column.classifier( data, rec, col ) );
617
- //}
664
+ let cls = "";
665
+ if( column.cellClassifier ) {
666
+ extra_cls.push( column.cellClassifier( row, col ) );
667
+ }
618
668
 
619
- //if (data instanceof Function) {
620
- // return data(rec, col);
621
- //}
669
+ if( data instanceof UnsafeHtml ) {
670
+ return data;
671
+ }
622
672
 
623
673
  if (column.formatter) {
624
674
  return column.formatter(data);
@@ -661,7 +711,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
661
711
 
662
712
  case "percent": {
663
713
  return new Box({
664
- cls: "percent" + cls,
714
+ cls: "percent " + cls,
665
715
  content: new Component({ cls: "bar", width: data + "%" })
666
716
  });
667
717
  }
@@ -733,8 +783,16 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
733
783
 
734
784
  els.push(el);
735
785
  }
786
+
787
+ let row_cls = 'row';
788
+ if( this.props.rowClassifier ) {
789
+ const xtra = this.props.rowClassifier( rowid );
790
+ if( xtra ) {
791
+ row_cls += ' ' + xtra.trim();
792
+ }
793
+ }
736
794
 
737
- return new Box({ cls: "row", style: { top: top.toFixed(2) + "px" }, content: els });
795
+ return new Box({ cls: row_cls, style: { top: top.toFixed(2) + "px" }, content: els });
738
796
  }
739
797
 
740
798
  /**
@@ -968,11 +1026,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
968
1026
  this._addSelection(ref.ref,true);
969
1027
  }
970
1028
 
971
- this._on_dblclk(e, ref.row, ref.col);
972
-
973
- debugger;
974
- //const rec = this._dataview.getByIndex( row );
975
- //this.fire( "dblClick", { context: rec } );
1029
+ this.fire( "dblClick", { context: { row: ref.row, col: ref.col } } );
976
1030
  }
977
1031
  });
978
1032
 
@@ -986,9 +1040,10 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
986
1040
  this._addSelection(ref.ref, true );
987
1041
  }
988
1042
 
989
- debugger;
990
- //const rec = this._dataview.getByIndex( row );
991
- //this.fire( "contextMenu", { uievent: e, context: rec } );
1043
+ this.fire( "contextMenu", { uievent: e, context: { row: ref.row, col: ref.col } } );
1044
+ }
1045
+ else {
1046
+ this.fire( "contextMenu", { uievent: e, context: null } );
992
1047
  }
993
1048
 
994
1049
  e.preventDefault();
@@ -1017,14 +1072,6 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
1017
1072
  this._computeFullSize();
1018
1073
  }
1019
1074
 
1020
- /**
1021
- *
1022
- */
1023
-
1024
- protected _on_dblclk(e: UIEvent, row: number, col: number) {
1025
-
1026
- }
1027
-
1028
1075
  /**
1029
1076
  *
1030
1077
  */
@@ -40,6 +40,7 @@
40
40
  &> .body {
41
41
  padding: 8px;
42
42
  border: 1px solid var( --border );
43
+ min-height: 0;
43
44
  }
44
45
  }
45
46
 
@@ -32,6 +32,7 @@ interface TextAreaProps extends BaseProps {
32
32
  value?: string;
33
33
  resize?: boolean;
34
34
  readonly?: boolean;
35
+ trim?: boolean;
35
36
  }
36
37
 
37
38
 
@@ -39,7 +40,7 @@ interface TextAreaProps extends BaseProps {
39
40
  *
40
41
  */
41
42
 
42
- class SimpleTextArea extends Component {
43
+ class SimpleTextArea extends Component<TextAreaProps> {
43
44
 
44
45
  constructor( props: TextAreaProps ) {
45
46
  super( { ...props, tag: "textarea" } );
@@ -61,7 +62,12 @@ class SimpleTextArea extends Component {
61
62
  }
62
63
 
63
64
  getText( ) {
64
- return (this.dom as HTMLTextAreaElement).value;
65
+ let text = (this.dom as HTMLTextAreaElement).value;
66
+ if( this.props.trim!==false ) {
67
+ text = text.trim();
68
+ }
69
+
70
+ return text;
65
71
  }
66
72
 
67
73
  queryInterface<T>(name: string): T {
@@ -88,7 +88,14 @@ export class TextEdit extends HBox {
88
88
  }
89
89
 
90
90
  const gadgets = props.inputGadgets ?? [];
91
+ gadgets.forEach( g => {
92
+ g.addClass( "gadget" );
93
+ })
94
+
95
+
91
96
  const iprops: InputProps = { ...props, id: props.inputId, attrs: props.inputAttrs, width: props.inputWidth };
97
+ // no propagation...
98
+ delete iprops.cls;
92
99
 
93
100
  this.setContent( [
94
101
  props.label ? new HBox( { id: "label", width: props.labelWidth, content: [
@@ -33,6 +33,7 @@ $color--interval: 8.4%;
33
33
  --color-primary-a90: #{darken($color--base, $color--interval * 4)};
34
34
  --color-primary-a100: #000000;
35
35
 
36
+ --color-danger-a30: #{lighten(#c01010,30%)};
36
37
  --color-danger-a50: #c01010;
37
38
  --color-danger-a60: #{darken(#c01010,10%)};
38
39
 
@@ -46,6 +47,7 @@ $color--interval: 8.4%;
46
47
  // Backgrounds
47
48
  --background-primary: var(--color-primary-a0); // Fond principal
48
49
  --background-secondary: var(--color-primary-a10); // Fond secondaire
50
+ --background-ternary: #f4f4f4; // Fond secondaire
49
51
 
50
52
  // Texts
51
53
  --text-primary: var(--color-primary-a100); // Texte principal
@@ -28,6 +28,7 @@ import icon from "./comments-question.svg"
28
28
 
29
29
  let last_hit: HTMLElement = null;
30
30
  let tooltip: Tooltip = null;
31
+ let mouse_pos: Point = {x:0, y:0 };
31
32
 
32
33
  const timer = new Timer( );
33
34
 
@@ -53,15 +54,20 @@ export function initTooltips( ) {
53
54
 
54
55
  }, true );
55
56
 
56
- document.addEventListener( "mouseleave", ( ev: Event ) => {
57
+ document.addEventListener( "mouseleave", ( ev: MouseEvent ) => {
57
58
  //console.log( "leave", ev.target );
58
-
59
59
  if( last_hit && ev.target==last_hit ) {
60
60
  last_hit = null;
61
61
  closeTT( );
62
62
  }
63
63
 
64
64
  }, true );
65
+
66
+ document.addEventListener( "mousemove", ( ev: MouseEvent ) => {
67
+ if( last_hit ) {
68
+ mouse_pos = { x:ev.pageX, y:ev.pageY }
69
+ }
70
+ });
65
71
  }
66
72
 
67
73
  function showTT( text: string, rc: Rect, pt: Point ) {
@@ -71,8 +77,14 @@ function showTT( text: string, rc: Rect, pt: Point ) {
71
77
 
72
78
  timer.setTimeout( null, 300, ( ) => {
73
79
  tooltip.setText( unsafeHtml(text) );
80
+
81
+ let y = mouse_pos.y;
82
+ if( rc.contains(mouse_pos) ) {
83
+ y = rc.bottom;
84
+ }
85
+
74
86
  //tooltip.displayNear( rc, "top left", "bottom left", {x:0,y:4} );
75
- tooltip.displayAt( pt.x+17, pt.y+17 );
87
+ tooltip.displayAt( mouse_pos.x+17, y+5 );
76
88
  } );
77
89
  }
78
90