x4js 2.3.17 → 2.3.19

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 (46) hide show
  1. package/aicli.md +3 -3
  2. package/aicontext.md +211 -47
  3. package/cli/config.mjs +14 -7
  4. package/cli/copy-plugin.mjs +8 -0
  5. package/cli/create.mjs +8 -2
  6. package/cli/dev.mjs +4 -1
  7. package/cli/less-plugin.mjs +5 -1
  8. package/cli/main.mjs +15 -5
  9. package/cli/rawfile-plugin.mjs +23 -3
  10. package/cli/templates.mjs +5 -1
  11. package/package.json +1 -1
  12. package/src/components/calendar/calendar.module.scss +16 -1
  13. package/src/components/calendar/calendar.ts +52 -9
  14. package/src/components/checkbox/checkbox.ts +1 -0
  15. package/src/components/combobox/combobox.ts +51 -23
  16. package/src/components/dialog/dialog.ts +4 -1
  17. package/src/components/form/form.ts +9 -5
  18. package/src/components/gridview/gridview.ts +352 -176
  19. package/src/components/input/input.ts +63 -38
  20. package/src/components/keyboard/keyboard.module.scss +35 -4
  21. package/src/components/keyboard/keyboard.ts +295 -80
  22. package/src/components/listbox/listbox.ts +6 -1
  23. package/src/components/messages/messages.ts +12 -3
  24. package/src/components/popup/popup.ts +19 -20
  25. package/src/components/radio/radio.ts +8 -4
  26. package/src/components/rating/rating.ts +38 -48
  27. package/src/components/slider/slider.ts +38 -8
  28. package/src/components/spreadsheet/spreadsheet.ts +167 -132
  29. package/src/components/switch/switch.ts +2 -1
  30. package/src/components/tag/tag.ts +1 -2
  31. package/src/components/textarea/textarea.module.scss +6 -2
  32. package/src/components/textarea/textarea.ts +50 -11
  33. package/src/components/textedit/textedit.ts +12 -2
  34. package/src/core/component.ts +16 -9
  35. package/src/core/core_application.ts +8 -4
  36. package/src/core/core_clipboard.ts +239 -0
  37. package/src/core/core_data.ts +118 -49
  38. package/src/core/core_dom.ts +36 -6
  39. package/src/core/core_dragdrop.ts +11 -5
  40. package/src/core/core_element.ts +1 -1
  41. package/src/core/core_pdf.ts +696 -46
  42. package/src/core/core_router.ts +42 -44
  43. package/src/core/core_shortcuts.ts +1 -1
  44. package/src/core/core_state.ts +22 -5
  45. package/src/x4.d.ts +1 -0
  46. package/src/x4.ts +1 -0
@@ -14,12 +14,11 @@
14
14
  * that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
15
15
  **/
16
16
 
17
- import { Component, ComponentEvents, componentFromDOM, ComponentProps, EvChange } from '../../core/component';
17
+ import { Component, ComponentEvents, ComponentProps, EvChange } from '../../core/component';
18
18
  import { EventCallback } from '../../core/core_events';
19
19
  import { HBox } from '../boxes/boxes';
20
- import { Input } from '../input/input';
21
20
  import { Icon } from '../icon/icon';
22
- import { class_ns } from '../../core/core_tools';
21
+ import { class_ns, IComponentInterface, IFormElement } from '../../core/core_tools';
23
22
 
24
23
  import icons from "../assets/icons"
25
24
 
@@ -54,50 +53,33 @@ export interface RatingProps extends ComponentProps {
54
53
  export class Rating extends HBox<RatingProps,RatingEventMap> {
55
54
 
56
55
  private m_els: Component[];
57
- private m_input: Input;
58
-
56
+
59
57
  constructor( props: RatingProps ) {
60
58
  super( props );
61
59
 
62
- props.steps = props.steps ?? 5;
60
+ // this.props: a copy of props (Box adds its tag)
61
+ this.props.steps = this.props.steps ?? 5;
62
+
63
+ // the name is on the rating: a Form finds it, and reads its value as a number (queryInterface)
64
+ if( props.name ) {
65
+ this.setAttribute( "name", props.name );
66
+ }
67
+
68
+ this.addDOMEvent( 'click', (e) => this._on_click(e) );
63
69
  this._update( );
64
70
  }
65
71
 
72
+ // value: the count of stars that are lit, 0 for none
66
73
  private _update( ) {
67
-
68
74
  const props = this.props;
69
-
70
- let shape = props.icon ?? icons.star.solid;
71
- let value = props.value ?? 0;
72
-
73
- this.m_input = new Input( {
74
- type: "text",
75
- hidden: true,
76
- name: props.name,
77
- value: ''+value
78
- } );
79
-
80
- this.addDOMEvent( 'click', (e) => this._on_click(e) );
75
+ const shape = props.icon ?? icons.star.solid;
76
+ const value = props.value ?? 0;
81
77
 
82
78
  this.m_els = [];
83
79
  for( let i=0; i<props.steps; i++ ) {
84
-
85
- let cls = 'item';
86
- if( i+1 <= value ) {
87
- cls += ' checked';
88
- }
89
-
90
- let c = new Icon( {
91
- cls,
92
- iconId: shape,
93
- } );
94
-
95
- c.setInternalData( "value", i );
96
-
97
- this.m_els.push( c );
80
+ this.m_els.push( new Icon( { cls: i < value ? 'item checked' : 'item', iconId: shape } ) );
98
81
  }
99
82
 
100
- this.m_els.push( this.m_input );
101
83
  this.setContent( this.m_els );
102
84
  }
103
85
 
@@ -107,12 +89,7 @@ export class Rating extends HBox<RatingProps,RatingEventMap> {
107
89
 
108
90
  setValue( v: number ) {
109
91
  this.props.value = v;
110
-
111
- for( let c=0; c<this.props.steps; c++ ) {
112
- this.m_els[c].setClass( 'checked', this.m_els[c].getInternalData('value')<=v );
113
- }
114
-
115
- this.m_input.setValue( ''+this.props.value );
92
+ this.m_els.forEach( ( c, i ) => c.setClass( 'checked', i < v ) );
116
93
  }
117
94
 
118
95
  setSteps( n: number ) {
@@ -121,18 +98,31 @@ export class Rating extends HBox<RatingProps,RatingEventMap> {
121
98
  }
122
99
 
123
100
  setShape( icon: string ) {
124
- this.removeClass( this.props.icon );
125
101
  this.props.icon = icon;
102
+ this._update( );
126
103
  }
127
104
 
105
+ // the star clicked: its svg, or the Icon itself
128
106
  private _on_click( ev: MouseEvent ) {
129
- let item = componentFromDOM( ev.target as HTMLElement );
130
- item = item.parentElement( Icon );
131
-
132
- if( item ) {
133
- this.setValue( item.getInternalData("value") );
107
+ const n = this.m_els.findIndex( c => c.dom.contains( ev.target as Node ) );
108
+ if( n>=0 ) {
109
+ // the star of the value, clicked again: none is lit
110
+ this.setValue( n+1===this.props.value ? 0 : n+1 );
111
+ this.fire( 'change', { value: this.props.value } );
134
112
  }
113
+ }
114
+
115
+ override queryInterface<T extends IComponentInterface>( name: string ): T {
116
+ if( name=="form-element" ) {
117
+ const i: IFormElement = {
118
+ getRawValue: ( ) => this.getValue( ),
119
+ setRawValue: ( v: any ) => { this.setValue( +v ); },
120
+ isValid: ( ) => true,
121
+ };
135
122
 
136
- this.fire( 'change', {value:this.props.value} );
137
- }
123
+ return i as unknown as T;
124
+ }
125
+
126
+ return super.queryInterface( name );
127
+ }
138
128
  }
@@ -16,7 +16,7 @@
16
16
 
17
17
  import { EventCallback } from '../../core/core_events';
18
18
  import { Component, ComponentEvents, ComponentProps, EvChange } from '../../core/component';
19
- import { class_ns, Rect } from '../../core/core_tools';
19
+ import { class_ns, IComponentInterface, IFormElement, Rect } from '../../core/core_tools';
20
20
 
21
21
  import { HBox } from '../boxes/boxes';
22
22
  import { Input } from '../input/input';
@@ -33,6 +33,8 @@ interface SliderProps extends ComponentProps {
33
33
  max: number;
34
34
  step?: number;
35
35
  vertical?: boolean;
36
+ /** for a Form: it reads the value as a number */
37
+ name?: string;
36
38
  change?: EventCallback<EvChange>;
37
39
  }
38
40
 
@@ -83,6 +85,12 @@ export class Slider extends Component<SliderProps,SliderEvents> {
83
85
 
84
86
  this.setAttribute( "tabindex", 0 );
85
87
 
88
+ // the name is on the slider: a Form finds it (queryInterface). The hidden range keeps
89
+ // the value: the browser brings it between min and max, on the step
90
+ if( props.name ) {
91
+ this.setAttribute( "name", props.name );
92
+ }
93
+
86
94
  this.setDOMEvents( {
87
95
  pointerdown: ( ev ) => this._on_mousedown( ev ),
88
96
  pointermove: ( ev ) => this._on_mousemove( ev ),
@@ -106,8 +114,8 @@ export class Slider extends Component<SliderProps,SliderEvents> {
106
114
  }
107
115
 
108
116
  private _getMinMax( ) {
109
- const min = parseInt( this._range.getAttribute( 'min' ) );
110
- const max = parseInt( this._range.getAttribute( 'max' ) );
117
+ const min = parseFloat( this._range.getAttribute( 'min' ) );
118
+ const max = parseFloat( this._range.getAttribute( 'max' ) );
111
119
  return [min,max];
112
120
  }
113
121
 
@@ -134,15 +142,17 @@ export class Slider extends Component<SliderProps,SliderEvents> {
134
142
  const [min,max] = this._getMinMax( );
135
143
 
136
144
  this._range.setNumValue( min + (perc * (max-min) ) );
137
- this._update( );
145
+ this._update( true );
138
146
  }
139
147
  }
140
148
 
141
- private _update( ) {
149
+ // notify: a change made by the user (drag, arrows); not by the code, nor at the construction
150
+ private _update( notify = false ) {
142
151
  const value = this._range.getNumValue( );
143
152
  const [min,max] = this._getMinMax( );
144
153
 
145
- let perc = max>min ? value / Math.abs(max-min) * 100 : 0;
154
+ // from min: min is at 0%, max at 100%
155
+ let perc = max>min ? (value-min) / (max-min) * 100 : 0;
146
156
 
147
157
  if( !this.props.vertical ) {
148
158
  this._thumb.setStyleValue( "left", perc+"%" );
@@ -155,7 +165,9 @@ export class Slider extends Component<SliderProps,SliderEvents> {
155
165
  this._bar.setStyleValue( "height", perc+"%" );
156
166
  }
157
167
 
158
- this.fire( "change", { value } );
168
+ if( notify ) {
169
+ this.fire( "change", { value } );
170
+ }
159
171
  }
160
172
 
161
173
  private _on_mouseup( ev: PointerEvent ) {
@@ -184,7 +196,7 @@ export class Slider extends Component<SliderProps,SliderEvents> {
184
196
  }
185
197
 
186
198
  this._range.setNumValue( this._range.getNumValue()+inc );
187
- this._update( );
199
+ this._update( true );
188
200
  }
189
201
  }
190
202
 
@@ -202,4 +214,22 @@ export class Slider extends Component<SliderProps,SliderEvents> {
202
214
  this._range.setNumValue( v );
203
215
  this._update( );
204
216
  }
217
+
218
+ getValue( ) {
219
+ return this._range.getNumValue( );
220
+ }
221
+
222
+ override queryInterface<T extends IComponentInterface>( name: string ): T {
223
+ if( name=="form-element" ) {
224
+ const i: IFormElement = {
225
+ getRawValue: ( ) => this.getValue( ),
226
+ setRawValue: ( v: any ) => { this.setValue( +v ); },
227
+ isValid: ( ) => true,
228
+ };
229
+
230
+ return i as unknown as T;
231
+ }
232
+
233
+ return super.queryInterface( name );
234
+ }
205
235
  }
@@ -348,6 +348,11 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
348
348
  this.lock(false);
349
349
  });
350
350
 
351
+ // out of the document: nothing is drawn, as before its first insertion
352
+ this.addDOMEvent("removed", () => {
353
+ this.lock(true);
354
+ });
355
+
351
356
  this.addDOMEvent("resized", () => {
352
357
  this._updateFlexs();
353
358
  this._computeFullSize();
@@ -358,6 +363,139 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
358
363
  this._on_key(e);
359
364
  })
360
365
 
366
+ // the handlers of the grid itself are put once, here. _init builds what is
367
+ // inside the grid, each time it comes in the document
368
+
369
+ // WHEEL
370
+ this.addDOMEvent("wheel", (ev: WheelEvent) => {
371
+ // many rows: one pixel of the scroll bar is one row, wherever the pointer is
372
+ if (ev.deltaY && this._store && this._store.getRowCount() >= SCROLL_LIMIT) {
373
+ this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -1 : 1);
374
+ ev.stopPropagation();
375
+ ev.preventDefault();
376
+ }
377
+ else if (this._has_fixed && ev.deltaY) {
378
+ // wheel on fixed part
379
+ // fixed part do not have scrollbar, so we need to handle it by hand
380
+ let t = ev.target as Node;
381
+ while (t != this.dom) {
382
+ if (t == this._vheader.dom) {
383
+ this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -this._row_height : this._row_height);
384
+ ev.stopPropagation();
385
+ ev.preventDefault();
386
+ break;
387
+ }
388
+
389
+ t = t.parentNode;
390
+ }
391
+ }
392
+ })
393
+
394
+ const targetCell = (e: MouseEvent) => {
395
+ let el = e.target as Element;
396
+ while (el && !el.classList.contains("cell")) {
397
+ el = el.parentElement;
398
+ }
399
+
400
+ if (el) {
401
+ const cel = componentFromDOM(el);
402
+ return {
403
+ ref: cel.getIntData("ref"),
404
+ row: cel.getInternalData("row"),
405
+ col: cel.getInternalData("col"),
406
+ }
407
+ }
408
+
409
+ return undefined;
410
+ }
411
+
412
+
413
+ // MOUSEDOWN
414
+ this.addDOMEvent("mousedown", (e) => {
415
+ if( !this._edit || this._inEditor(e) ) {
416
+ return;
417
+ }
418
+
419
+ // click on another cell while editing:
420
+ // validate now, rows may be rebuilt before the click event (and the click lost)
421
+ const ref = targetCell(e);
422
+ if( ref && ref.row!==undefined ) {
423
+ this._endEdit( true, true );
424
+
425
+ if (!this._selection.has(ref.ref)) {
426
+ this._clearSelection( false );
427
+ this._addSelection(ref.ref,true);
428
+ }
429
+
430
+ e.preventDefault( );
431
+ }
432
+ });
433
+
434
+ // CLICK
435
+ this.addDOMEvent("click", (e) => {
436
+ if( this._inEditor(e) ) {
437
+ return;
438
+ }
439
+
440
+ const ref = targetCell(e);
441
+ if (ref) {
442
+ //TODO: multiselection
443
+ if (!this._selection.has(ref.ref)) {
444
+ this._clearSelection( false );
445
+ this._addSelection(ref.ref,true);
446
+ }
447
+ }
448
+ });
449
+
450
+ // DBLCLICK
451
+ this.addDOMEvent("dblclick", (e) => {
452
+ if( this._inEditor(e) ) {
453
+ return;
454
+ }
455
+
456
+ const ref = targetCell(e);
457
+ if (ref) {
458
+ //TODO: multiselection
459
+ if (!this._selection.has(ref.ref)) {
460
+ this._clearSelection(false);
461
+ this._addSelection(ref.ref,true);
462
+ }
463
+
464
+ const dev: EvDblClick = { context: { row: ref.row, col: ref.col } };
465
+ this.fire( "dblClick", dev );
466
+
467
+ // checkboxes are toggled, other cells are edited
468
+ if( !dev.defaultPrevented && !this._toggleCell( ref.row, ref.col ) ) {
469
+ this.editCell( ref.row, ref.col );
470
+ }
471
+ }
472
+ });
473
+
474
+ // CONTEXT
475
+ this.addDOMEvent("contextmenu", (e) => {
476
+ // keep the native menu of the editor (copy/paste)
477
+ if( this._inEditor(e) ) {
478
+ return;
479
+ }
480
+
481
+ const ref = targetCell(e);
482
+ if (ref) {
483
+ //TODO: multiselection
484
+ if (!this._selection.has(ref.ref)) {
485
+ this._clearSelection( false );
486
+ this._addSelection(ref.ref, true );
487
+ }
488
+
489
+ this.fire( "contextMenu", { uievent: e, context: { row: ref.row, col: ref.col } } );
490
+ }
491
+ else {
492
+ this.fire( "contextMenu", { uievent: e, context: null } );
493
+ }
494
+
495
+ e.preventDefault();
496
+ e.stopPropagation();
497
+ });
498
+
361
499
  if (props.store) {
362
500
  this.setStore(props.store);
363
501
  }
@@ -643,7 +781,8 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
643
781
  if (lock) {
644
782
  this._lock++;
645
783
  }
646
- else {
784
+ // an unlock too many leaves it at 0: the grid goes on drawing
785
+ else if (this._lock > 0) {
647
786
  if (--this._lock == 0 && this._dirty) {
648
787
  this._update(true);
649
788
  }
@@ -676,7 +815,9 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
676
815
  const sizer = new CSizer("right");
677
816
 
678
817
  sizer.on("stop", () => {
818
+ // the flex columns took what was left: the body gets the width of its columns
679
819
  this._updateFlexs();
820
+ this._computeFullSize();
680
821
  })
681
822
 
682
823
  sizer.on("resize", (ev) => {
@@ -698,6 +839,24 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
698
839
  }
699
840
  })
700
841
 
842
+ // a double click on the sizer: the column takes its share of the place
843
+ // that is left (flex 1). To stop taking it, the sizer is dragged
844
+ sizer.addDOMEvent("dblclick", (ev) => {
845
+ // both: the grid has a double click of its own
846
+ ev.stopPropagation();
847
+ ev.preventDefault();
848
+
849
+ // a fixed column is not in the place that is shared
850
+ if (fixed || cdata.flex) {
851
+ return;
852
+ }
853
+
854
+ cdata.flex = 1;
855
+
856
+ this._updateFlexs();
857
+ this._computeFullSize();
858
+ });
859
+
701
860
  const cell = new Component({
702
861
  cls: `cell`,
703
862
  attrs: { "data-col": col },
@@ -956,6 +1115,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
956
1115
 
957
1116
  const el = new Component({
958
1117
  cls: "cell",
1118
+ attrs: { "data-col": col }, // the sizer of the column finds its cells by it
959
1119
  style: { width: cdata?.width ? cdata.width + "px" : undefined, justifyContent: align },
960
1120
  content
961
1121
  });
@@ -993,7 +1153,12 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
993
1153
  for (let x = 0; x < ccount; x++) {
994
1154
  const cdata = this._getCol(x);
995
1155
 
996
- if (!cdata.fixed && cdata.flex) {
1156
+ // the fixed columns are not in the viewport: their width is not taken from its own
1157
+ if (cdata.fixed) {
1158
+ continue;
1159
+ }
1160
+
1161
+ if (cdata.flex) {
997
1162
  flexc += cdata.flex;
998
1163
  }
999
1164
  else {
@@ -1095,136 +1260,6 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
1095
1260
  this._update()
1096
1261
  });
1097
1262
 
1098
- // WHEEL
1099
- this.addDOMEvent("wheel", (ev: WheelEvent) => {
1100
- if (ev.deltaY && this._store && this._store.getRowCount() >= SCROLL_LIMIT) {
1101
- this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -1 : 1);
1102
- ev.stopPropagation();
1103
- ev.preventDefault();
1104
- }
1105
-
1106
- if (this._has_fixed && ev.deltaY) {
1107
- // wheel on fixed part
1108
- // fixed part do not have scrollbar, so we need to handle it by hand
1109
- let t = ev.target as Node;
1110
- while (t != this.dom) {
1111
- if (t == this._vheader.dom) {
1112
- this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -this._row_height : this._row_height);
1113
- ev.stopPropagation();
1114
- ev.preventDefault();
1115
- break;
1116
- }
1117
-
1118
- t = t.parentNode;
1119
- }
1120
- }
1121
- })
1122
-
1123
- const targetCell = (e: MouseEvent) => {
1124
- let el = e.target as Element;
1125
- while (el && !el.classList.contains("cell")) {
1126
- el = el.parentElement;
1127
- }
1128
-
1129
- if (el) {
1130
- const cel = componentFromDOM(el);
1131
- return {
1132
- ref: cel.getIntData("ref"),
1133
- row: cel.getInternalData("row"),
1134
- col: cel.getInternalData("col"),
1135
- }
1136
- }
1137
-
1138
- return undefined;
1139
- }
1140
-
1141
-
1142
- // MOUSEDOWN
1143
- this.addDOMEvent("mousedown", (e) => {
1144
- if( !this._edit || this._inEditor(e) ) {
1145
- return;
1146
- }
1147
-
1148
- // click on another cell while editing:
1149
- // validate now, rows may be rebuilt before the click event (and the click lost)
1150
- const ref = targetCell(e);
1151
- if( ref && ref.row!==undefined ) {
1152
- this._endEdit( true, true );
1153
-
1154
- if (!this._selection.has(ref.ref)) {
1155
- this._clearSelection( false );
1156
- this._addSelection(ref.ref,true);
1157
- }
1158
-
1159
- e.preventDefault( );
1160
- }
1161
- });
1162
-
1163
- // CLICK
1164
- this.addDOMEvent("click", (e) => {
1165
- if( this._inEditor(e) ) {
1166
- return;
1167
- }
1168
-
1169
- const ref = targetCell(e);
1170
- if (ref) {
1171
- //TODO: multiselection
1172
- if (!this._selection.has(ref.ref)) {
1173
- this._clearSelection( false );
1174
- this._addSelection(ref.ref,true);
1175
- }
1176
- }
1177
- });
1178
-
1179
- // DBLCLICK
1180
- this.addDOMEvent("dblclick", (e) => {
1181
- if( this._inEditor(e) ) {
1182
- return;
1183
- }
1184
-
1185
- const ref = targetCell(e);
1186
- if (ref) {
1187
- //TODO: multiselection
1188
- if (!this._selection.has(ref.ref)) {
1189
- this._clearSelection(false);
1190
- this._addSelection(ref.ref,true);
1191
- }
1192
-
1193
- const dev: EvDblClick = { context: { row: ref.row, col: ref.col } };
1194
- this.fire( "dblClick", dev );
1195
-
1196
- // checkboxes are toggled, other cells are edited
1197
- if( !dev.defaultPrevented && !this._toggleCell( ref.row, ref.col ) ) {
1198
- this.editCell( ref.row, ref.col );
1199
- }
1200
- }
1201
- });
1202
-
1203
- // CONTEXT
1204
- this.addDOMEvent("contextmenu", (e) => {
1205
- // keep the native menu of the editor (copy/paste)
1206
- if( this._inEditor(e) ) {
1207
- return;
1208
- }
1209
-
1210
- const ref = targetCell(e);
1211
- if (ref) {
1212
- //TODO: multiselection
1213
- if (!this._selection.has(ref.ref)) {
1214
- this._clearSelection( false );
1215
- this._addSelection(ref.ref, true );
1216
- }
1217
-
1218
- this.fire( "contextMenu", { uievent: e, context: { row: ref.row, col: ref.col } } );
1219
- }
1220
- else {
1221
- this.fire( "contextMenu", { uievent: e, context: null } );
1222
- }
1223
-
1224
- e.preventDefault();
1225
- e.stopPropagation();
1226
- });
1227
-
1228
1263
  this._updateFlexs();
1229
1264
 
1230
1265
  this._fheader = this._buildColHeader(true);
@@ -53,7 +53,8 @@ export class Switch extends HBox<SwitchProps> {
53
53
  new Component( {
54
54
  cls: "switch",
55
55
  content: [
56
- new Input( { type: "checkbox", id: inputId, name: props.name, checked: props.checked } ),
56
+ // value: sent by a <form> of the browser when checked; a Form of x4 reads a boolean
57
+ new Input( { type: "checkbox", id: inputId, name: props.name, value: props.value, checked: props.checked } ),
57
58
  new Component( { cls: "track" } ),
58
59
  new Component( { cls: "thumb" } ),
59
60
  ]
@@ -34,8 +34,7 @@ export interface TagProps extends ComponentProps {
34
34
  * @cssvar
35
35
  * ```
36
36
  * --tag-border
37
- * --tag-border-focus
38
- * --tag-selection
37
+ * --tag-background
39
38
  * --tag-color
40
39
  * --tag-icon-color
41
40
  * ```
@@ -24,6 +24,8 @@
24
24
 
25
25
  .x4simpletextarea {
26
26
 
27
+ flex-shrink: 0; // alone in a box, it keeps its height: the box does not crush it
28
+ resize: none; // no handle unless the prop resize asks for one
27
29
  padding: 4px;
28
30
  outline: none;
29
31
  font: inherit;
@@ -39,8 +41,10 @@
39
41
  color: white;
40
42
  }
41
43
 
42
- &[resize=false] {
43
- resize: none;
44
+ // as the one of an input
45
+ &::placeholder {
46
+ color: var( --input-placeholder );
47
+ font-style: italic;
44
48
  }
45
49
  }
46
50