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
@@ -15,22 +15,24 @@
15
15
  **/
16
16
 
17
17
  import { EventCallback } from '../../core/core_events';
18
- import { Component, ComponentEvent, componentFromDOM, ComponentProps, EvChange, EvFocus } from '../../core/component';
18
+ import { Component, ComponentEvent, ComponentProps, EvChange, EvFocus } from '../../core/component';
19
19
  import { class_ns, formatIntlDate, IComponentInterface, IFormElement, isString } from '../../core/core_tools';
20
20
 
21
21
  import "./input.module.scss"
22
22
 
23
23
 
24
- function getRadioOwner( el: Element ) {
24
+ /**
25
+ * @internal where to look for the radios of a group: the form that holds them,
26
+ * else the document
27
+ */
25
28
 
26
- while( el!=document.body ) {
27
- const comp = componentFromDOM(el);
28
- const ifx = comp.queryInterface( "tab-handler");
29
- if( ifx ) {
30
- return el;
31
- }
29
+ export function getRadioOwner( el: Element ): ParentNode {
32
30
 
33
- el = el.parentElement;
31
+ // two forms of a page can each have a group of the same name;
32
+ // a dialog has its form, its radios are in it
33
+ const form = el.closest( "form" );
34
+ if( form ) {
35
+ return form;
34
36
  }
35
37
 
36
38
  return document;
@@ -222,7 +224,19 @@ export class Input extends Component<InputProps,InputEvents> {
222
224
  if( props.autofocus===true ) {
223
225
  this.setAttribute( "autofocus", true );
224
226
  }
225
-
227
+
228
+ // every type but checkbox and radio (range and file ignore readonly)
229
+ if( props.type!="checkbox" && props.type!="radio" ) {
230
+ this.setAttribute( "required", props.required );
231
+ this.setAttribute( "readonly", props.readonly );
232
+ }
233
+
234
+ // placeholder: shown by the browser on text, email, password and number only,
235
+ // not on date nor time (they show their own pattern)
236
+ if( props.type===undefined || props.type=="text" || props.type=="email" || props.type=="password" || props.type=="number" ) {
237
+ this.setAttribute( "placeholder", props.placeholder );
238
+ }
239
+
226
240
  switch( props.type ) {
227
241
  case "checkbox":
228
242
  case "radio": {
@@ -244,9 +258,7 @@ export class Input extends Component<InputProps,InputEvents> {
244
258
 
245
259
  case "number": {
246
260
  const p = this.props as NumberProps;
247
-
248
- this.setAttribute( "required", p.required );
249
- this.setAttribute( "readonly", p.readonly );
261
+
250
262
  this.setAttribute( "min", p.min );
251
263
  this.setAttribute( "max", p.max );
252
264
  this.setAttribute( "step", p.step );
@@ -256,20 +268,26 @@ export class Input extends Component<InputProps,InputEvents> {
256
268
  this.addDOMEvent( "wheel", ( e: WheelEvent ) => {
257
269
  if( this.hasFocus() ) { // only if has focus
258
270
  e.preventDefault( );
259
- let v = this.getNumValue();
260
- const delta = e.deltaY < 0 ? 1 : -1;
261
- v += (p.step ? p.step : 1) * delta;
262
- this.setNumValue( v, -2 );
263
271
 
264
- this.dom.dispatchEvent(new Event('input'));
272
+ // the browser keeps the value between min and max, on the step,
273
+ // and starts from a valid one when the field is empty
274
+ const input = this.dom as HTMLInputElement;
275
+ if( e.deltaY < 0 ) {
276
+ input.stepUp( );
277
+ }
278
+ else {
279
+ input.stepDown( );
280
+ }
281
+
282
+ // the decimals of the step: 10.1 shown 10.10 for a step of 0.01
283
+ this.setNumValue( this.getNumValue( ), -2 );
284
+ this.dom.dispatchEvent(new Event('input'));
265
285
  }
266
286
  }, );
267
287
  break;
268
288
  }
269
289
 
270
290
  case "date": {
271
- this.setAttribute( "required", props.required );
272
-
273
291
  let v = props.value;
274
292
  if( v instanceof Date ) {
275
293
  this.setAttribute( "value", formatIntlDate( v, "Y-M-D" ) );
@@ -295,7 +313,6 @@ export class Input extends Component<InputProps,InputEvents> {
295
313
  }
296
314
 
297
315
  case "time": {
298
- this.setAttribute( "required", props.required );
299
316
  if( props.value!==null && props.value!==undefined ) {
300
317
  this.setAttribute( "value", props.value );
301
318
  }
@@ -303,9 +320,6 @@ export class Input extends Component<InputProps,InputEvents> {
303
320
  }
304
321
 
305
322
  default: {
306
- this.setAttribute( "required", props.required );
307
- this.setAttribute( "readonly", props.readonly );
308
-
309
323
  if( props.value!==null && props.value!==undefined ) {
310
324
  this.setAttribute( "value", props.value );
311
325
  }
@@ -314,10 +328,6 @@ export class Input extends Component<InputProps,InputEvents> {
314
328
  this.setAttribute( "pattern", props.pattern );
315
329
  }
316
330
 
317
- if( props.placeholder!==null && props.placeholder!==undefined ) {
318
- this.setAttribute( "placeholder", props.placeholder );
319
- }
320
-
321
331
  if( props.spellcheck===false ) {
322
332
  this.setAttribute( "spellcheck", false );
323
333
  }
@@ -417,14 +427,19 @@ export class Input extends Component<InputProps,InputEvents> {
417
427
  }
418
428
 
419
429
  if( ndec==-2 && this.props.type=='number' ) {
420
- const p = this.props as NumberProps;
430
+ const step = (this.props as NumberProps).step;
431
+
432
+ // as many decimals as the step, 4 at most: the step in 1/10000 is an integer,
433
+ // each 0 on its right is a decimal less (0.25 → 2500 → 2, 2.5 → 25000 → 1, 2 → 0)
434
+ if( step!==undefined && step!=1 ) {
435
+ let n = Math.round( step * 10000 );
436
+ let decimals = 4;
437
+ while( n && n%10==0 && decimals ) {
438
+ n /= 10;
439
+ decimals--;
440
+ }
421
441
 
422
- if( p.step<1 ) {
423
- let ndec = -Math.floor(Math.log10(p.step ?? 1) );
424
- return this.setValue( value.toFixed(ndec) );
425
- }
426
- else if( p.step>1 ) {
427
- return this.setValue( value.toFixed() );
442
+ return this.setValue( value.toFixed( decimals ) );
428
443
  }
429
444
  }
430
445
  else if( ndec>=0 ) {
@@ -506,9 +521,16 @@ export class Input extends Component<InputProps,InputEvents> {
506
521
  override queryInterface<T extends IComponentInterface>( name: string ): T {
507
522
  if( name=="form-element" ) {
508
523
  const i: IFormElement = {
509
- getRawValue: ( ): any => {
524
+ getRawValue: ( ): any => {
525
+ // checkbox without value, or a boolean one: a boolean; with one: the value when checked,
526
+ // null when not (as a <form>)
510
527
  if( this.props.type=='checkbox' ) {
511
- return this.getCheck( );
528
+ const value = this.props.value;
529
+ if( value===undefined || typeof value==="boolean" ) {
530
+ return this.getCheck( );
531
+ }
532
+
533
+ return this.getCheck( ) ? value : null;
512
534
  }
513
535
  else if( this.props.type=='radio' ) {
514
536
  const owner = getRadioOwner( this.dom );
@@ -523,8 +545,11 @@ export class Input extends Component<InputProps,InputEvents> {
523
545
  }
524
546
  },
525
547
  setRawValue: ( v: any ) => {
548
+ // checkbox with a value (not a boolean): checked by that value (or true)
526
549
  if( this.props.type=='checkbox' ) {
527
- this.setCheck( !!v );
550
+ const value = this.props.value;
551
+ const plain = value===undefined || typeof value==="boolean";
552
+ this.setCheck( plain ? !!v : ( v===value || v===true ) );
528
553
  }
529
554
  else if( this.props.type=='radio' ) {
530
555
  if( this.props.value==v ) {
@@ -17,6 +17,9 @@
17
17
  :root {
18
18
  --keyboard-key-background: #33302d;
19
19
  --keyboard-hilite-color: #5ba921;
20
+ --keyboard-key-color: #ffffff;
21
+ --keyboard-accents-color: #7cc7ff;
22
+ --keyboard-symbols-color: #f2c94c;
20
23
  --keyboard-zoom: 100%;
21
24
  }
22
25
 
@@ -62,7 +65,8 @@
62
65
  width: 100%;
63
66
  }
64
67
 
65
- color: white;
68
+ // the color of the page, for the keys of characters
69
+ color: var( --kb-page-color, var( --keyboard-key-color ) );
66
70
  text-align: center;
67
71
 
68
72
  background-color: var( --keyboard-key-background);
@@ -83,6 +87,29 @@
83
87
  }
84
88
 
85
89
 
90
+ .accents {
91
+ --kb-page-color: var( --keyboard-accents-color );
92
+ }
93
+
94
+ .symbols {
95
+ --kb-page-color: var( --keyboard-symbols-color );
96
+ }
97
+
98
+ // the keys of commands keep their color, the keys that call a page have its one
99
+ .cnum,
100
+ .calpha,
101
+ .cspace {
102
+ color: var( --keyboard-key-color );
103
+ }
104
+
105
+ .caccents {
106
+ color: var( --keyboard-accents-color );
107
+ }
108
+
109
+ .csymbols {
110
+ color: var( --keyboard-symbols-color );
111
+ }
112
+
86
113
  .cplace {
87
114
  visibility: hidden;
88
115
  }
@@ -109,8 +136,9 @@
109
136
 
110
137
  // color: rgba(255,255,255,0.3);
111
138
 
112
- .lower,
113
- .upper {
139
+ .alpha,
140
+ .accents,
141
+ .symbols {
114
142
 
115
143
  .cspace {
116
144
  flex-basis: 689px;
@@ -128,7 +156,10 @@
128
156
  flex-basis: 88px;
129
157
  }
130
158
 
131
- .cnum {
159
+ .cnum,
160
+ .caccents,
161
+ .csymbols,
162
+ .calpha {
132
163
  flex-basis: 88px;
133
164
  }
134
165
  }