x4js 2.3.27 → 2.3.29

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 (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/aicontext.md +412 -24
  4. package/package.json +8 -3
  5. package/src/components/autocomplete/autocomplete.ts +5 -1
  6. package/src/components/bargraph/bargraph.module.scss +44 -0
  7. package/src/components/bargraph/bargraph.ts +362 -0
  8. package/src/components/base.scss +71 -1
  9. package/src/components/boxes/boxes.ts +2 -2
  10. package/src/components/button/button.module.scss +4 -3
  11. package/src/components/calendar/calendar.module.scss +6 -5
  12. package/src/components/calendar/calendar.ts +14 -8
  13. package/src/components/canvas/canvas.ts +5 -4
  14. package/src/components/carousel/carousel.module.scss +165 -0
  15. package/src/components/carousel/carousel.ts +423 -0
  16. package/src/components/chart/chart.module.scss +8 -0
  17. package/src/components/chart/chart.ts +170 -12
  18. package/src/components/checkbox/checkbox.module.scss +67 -1
  19. package/src/components/checkbox/checkbox.ts +22 -6
  20. package/src/components/colorinput/colorinput.module.scss +1 -0
  21. package/src/components/combobox/combobox.module.scss +6 -2
  22. package/src/components/components.ts +3 -0
  23. package/src/components/gantt/gantt.module.scss +462 -0
  24. package/src/components/gantt/gantt.ts +1254 -0
  25. package/src/components/gridview/gridview.module.scss +6 -0
  26. package/src/components/input/input.module.scss +1 -1
  27. package/src/components/knob/knob.module.scss +113 -0
  28. package/src/components/knob/knob.ts +355 -0
  29. package/src/components/mapview/mapview.module.scss +177 -0
  30. package/src/components/mapview/mapview.ts +764 -0
  31. package/src/components/messages/messages.module.scss +9 -4
  32. package/src/components/planner/planner.module.scss +688 -0
  33. package/src/components/planner/planner.ts +1792 -0
  34. package/src/components/propgrid/progrid.module.scss +13 -5
  35. package/src/components/radio/radio.module.scss +58 -32
  36. package/src/components/radio/radio.ts +12 -1
  37. package/src/components/sizers/sizer.ts +17 -5
  38. package/src/components/slider/slider.module.scss +22 -20
  39. package/src/components/spreadsheet/spreadsheet.module.scss +6 -0
  40. package/src/components/switch/switch.module.scss +68 -44
  41. package/src/components/switch/switch.ts +6 -3
  42. package/src/components/tabs/tabs.module.scss +2 -0
  43. package/src/components/tabs/tabs.ts +1 -0
  44. package/src/components/tag/tag.module.scss +4 -4
  45. package/src/components/textarea/textarea.module.scss +9 -0
  46. package/src/components/textarea/textarea.ts +1 -0
  47. package/src/components/textedit/textedit.module.scss +5 -2
  48. package/src/components/themes.scss +20 -6
  49. package/src/components/treeview/treeview.module.scss +6 -1
  50. package/src/components/treeview/treeview.ts +1 -0
  51. package/src/components/video/video.module.scss +5 -2
  52. package/src/components/video/video.ts +14 -1
  53. package/src/core/core_i18n.ts +65 -0
  54. package/src/core/core_pdf.ts +17 -0
  55. package/src/core/core_react.ts +12 -18
  56. package/src/core/core_svg.ts +9 -6
  57. package/src/core/core_tools.ts +44 -0
  58. package/src/x4doc.ts +3 -0
@@ -23,6 +23,7 @@
23
23
  --textarea-selection-color: var( --accent-color );
24
24
  --textarea-background: var( --background-input );
25
25
  --textarea-color: var( --text-primary );
26
+ --textarea-required: var( --alert-background );
26
27
  }
27
28
 
28
29
  .x4simpletextarea {
@@ -77,6 +78,14 @@
77
78
  display: none;
78
79
  }
79
80
 
81
+ // required: a "*" before the label, as the one of a TextEdit
82
+ &:has( > .x4simpletextarea[required] ) > .x4label::before {
83
+ content: "*";
84
+ font-weight: bold;
85
+ color: var( --textarea-required );
86
+ margin-right: 2px;
87
+ }
88
+
80
89
  .x4simpletextarea {
81
90
  // its height of rows when the TextArea has none, the TextArea's height when it has one;
82
91
  // a basis of 0 would leave it the height of its padding: a textarea scrolls, its min-height is 0
@@ -139,6 +139,7 @@ export class SimpleTextArea extends Component<SimpleTextAreaProps,TextAreaEvents
139
139
  * --textarea-selection-color
140
140
  * --textarea-background
141
141
  * --textarea-color
142
+ * --textarea-required
142
143
  * ```
143
144
  */
144
145
 
@@ -21,7 +21,7 @@
21
21
  --textedit-border-focus: var( --border-focus );
22
22
  --textedit-required: var( --alert-background );
23
23
 
24
- --textedit-btn-background: inherit;
24
+ --textedit-btn-background: var( --background-input ); // the one of its field: inherit was the page (dark theme)
25
25
  --textedit-btn-color: var( --text-secondary );
26
26
  --textedit-btn-color-hover: var( --text-primary );
27
27
 
@@ -64,6 +64,7 @@
64
64
  .x4button {
65
65
  margin: 0;
66
66
  padding: 0;
67
+ align-self: stretch; // the height of the field: no strip of the page above and under it
67
68
  background-color: var( --textedit-btn-background );
68
69
  color: var( --textedit-btn-color );
69
70
 
@@ -108,7 +109,9 @@
108
109
  color: var( --textedit-color-disabled );
109
110
  }
110
111
 
111
- #edit input {
112
+ // the field and its gadgets lose their background together
113
+ #edit input,
114
+ #edit .x4button {
112
115
  color: var( --textedit-color-disabled );
113
116
  background-color: unset;
114
117
  }
@@ -19,6 +19,9 @@
19
19
  $color--base: #569cd6;
20
20
  $color--interval: 8.4%;
21
21
 
22
+ // the blues of the dark theme, from their own base
23
+ $color--dark-base: #5796ca;
24
+
22
25
  :root {
23
26
  --color-primary-a0: #ffffff;
24
27
  --color-primary-a5: #{color.adjust($color--base, $lightness: 35%)};
@@ -52,6 +55,7 @@ $color--interval: 8.4%;
52
55
  --background-primary: var(--color-primary-a0);
53
56
  --background-secondary: var(--color-primary-a10);
54
57
  --background-ternary: color-mix(in srgb, var(--color-primary-a100) 4.3%, var(--color-primary-a0)); // #f4f4f4
58
+ --background-odd: color-mix(in srgb, var(--color-primary-a100) 2.4%, var(--color-primary-a0)); // #f9f9f9, a row out of two
55
59
  --background-hover: color-mix(in srgb, var(--color-primary-a100) 6.7%, var(--color-primary-a0)); // #eee
56
60
  --background-active: color-mix(in srgb, var(--color-primary-a100) 20%, var(--color-primary-a0)); // #ccc
57
61
  --background-input: color-mix(in srgb, var(--color-primary-a0) 60%, transparent); // white at 60%
@@ -118,12 +122,16 @@ html.x4theme-dark {
118
122
  --color-primary-a10: #2a2d2e; // list.hoverBackground
119
123
  --color-primary-a20: #37373d; // list.inactiveSelectionBackground
120
124
  --color-primary-a30: #3c3c3c; // input.border
121
- --color-primary-a40: #04395e; // list.activeSelectionBackground
122
- --color-primary-a50: #0078d4; // button.background, focusBorder
123
- --color-primary-a55: #026ec1; // button.hoverBackground
124
- --color-primary-a60: #0078d4;
125
- --color-primary-a70: #026ec1;
126
- --color-primary-a80: #04395e;
125
+
126
+ // the blues: from $color--dark-base (chosen); where VS Code has list.activeSelectionBackground,
127
+ // button.background and focusBorder, button.hoverBackground
128
+ --color-primary-a40: #{color.adjust($color--dark-base, $lightness: -35%)}; // a selection
129
+ --color-primary-a50: #{$color--dark-base}; // a button, the focus
130
+ --color-primary-a55: #{color.adjust($color--dark-base, $lightness: -5%)}; // a button hovered
131
+ --color-primary-a60: #{$color--dark-base};
132
+ --color-primary-a70: #{color.adjust($color--dark-base, $lightness: -5%)};
133
+ --color-primary-a80: #{color.adjust($color--dark-base, $lightness: -35%)};
134
+
127
135
  --color-primary-a90: #cccccc;
128
136
  --color-primary-a100: #cccccc; // foreground
129
137
 
@@ -155,5 +163,11 @@ html.x4theme-dark {
155
163
  --color-dialog-caption-text: #cccccc; // titleBar.activeForeground
156
164
  --color-dialog-background: #202020; // editorWidget.background
157
165
 
166
+ // the tiles of a MapView are light: turned dark, their colours kept
167
+ --mapview-tile-filter: invert( 1 ) hue-rotate( 180deg ) brightness( 0.95 ) contrast( 0.9 );
168
+
169
+ // the marks of the events out of the view of a Planner: the darkest blue (a90 is the text here)
170
+ --planner-edge-background: var( --accent-background-active );
171
+
158
172
  color-scheme: dark; // the native parts: select, scroll bars, number spinners
159
173
  }
@@ -20,6 +20,7 @@
20
20
  --treeview-background: var( --background-primary );
21
21
 
22
22
  --treeitem-color: var( --text-primary );
23
+ --treeitem-icon-color: var( --icon-color );
23
24
 
24
25
  --treeitem-backgound-hover: var( --background-secondary );
25
26
  --treeitem-color-hover: var( --text-primary );
@@ -79,7 +80,7 @@
79
80
 
80
81
  &>.label {
81
82
  --label-color: var( --treeitem-color );
82
- --label-icon-color: var( --treeitem-color );
83
+ --label-icon-color: var( --treeitem-icon-color );
83
84
 
84
85
  gap: 4px;
85
86
 
@@ -210,6 +211,10 @@
210
211
  border-color: var( --border );
211
212
  color: var( --treeview-item-color-disabled );
212
213
 
214
+ // the texts and the icons of the items
215
+ --treeitem-color: var( --treeview-item-color-disabled );
216
+ --treeitem-icon-color: var( --treeview-item-color-disabled );
217
+
213
218
  &>.body {
214
219
  .x4viewport {
215
220
  pointer-events: none;
@@ -73,6 +73,7 @@ export interface TreeviewEvents extends ComponentEvents {
73
73
  * ```
74
74
  * --treeview-background
75
75
  * --treeitem-color
76
+ * --treeitem-icon-color
76
77
  * --treeitem-backgound-hover
77
78
  * --treeitem-color-hover
78
79
  * --treeitem-background-sel
@@ -20,11 +20,14 @@
20
20
  background-color: black;
21
21
  position: relative;
22
22
 
23
+ // the size of the box, whatever the size of the stream (a <video> keeps its own size
24
+ // with only left / right): the image fitted in it, black bands around
23
25
  .vplayer {
24
26
  position: absolute;
25
27
  left: 0;
26
28
  top: 0;
27
- right: 0;
28
- bottom: 0;
29
+ width: 100%;
30
+ height: 100%;
31
+ object-fit: contain;
29
32
  }
30
33
  }
@@ -23,7 +23,8 @@ interface VideoProps extends ComponentProps {
23
23
  autoplay?: boolean;
24
24
  muted?: boolean;
25
25
  playsInline?: boolean;
26
- stream: MediaStream;
26
+ src?: string; // a file to play (url); a stream, when there is one, is shown instead
27
+ stream?: MediaStream; // a camera, a screen, a canvas (captureStream)
27
28
  }
28
29
 
29
30
  /**
@@ -55,6 +56,10 @@ export class Video extends Box {
55
56
  this.playsInline = props.playsInline;
56
57
  }
57
58
 
59
+ if( props.src ) {
60
+ this.src = props.src;
61
+ }
62
+
58
63
  this.stream = props.stream;
59
64
  }
60
65
 
@@ -86,6 +91,14 @@ export class Video extends Box {
86
91
  this.player.playsInline = set;
87
92
  }
88
93
 
94
+ get src( ) {
95
+ return this.player.src;
96
+ }
97
+
98
+ set src( url: string ) {
99
+ this.player.src = url;
100
+ }
101
+
89
102
  set stream( stream: MediaStream ) {
90
103
  this.player.srcObject = stream;
91
104
  }
@@ -379,6 +379,23 @@ let fr = {
379
379
  warning: '{0} avertissement',
380
380
  warnings: '{0} avertissements',
381
381
  },
382
+
383
+ carousel: {
384
+ carousel: 'carrousel',
385
+ slide: 'diapositive',
386
+ prev: 'Précédent',
387
+ next: 'Suivant',
388
+ position: '{0} sur {1}',
389
+ },
390
+
391
+ // the titles of the columns of a Gantt, by their id
392
+ gantt: {
393
+ title: 'Tâche',
394
+ start: 'Début',
395
+ end: 'Fin',
396
+ duration: 'Durée',
397
+ progress: 'Avancement',
398
+ },
382
399
  }
383
400
  };
384
401
 
@@ -457,6 +474,22 @@ let en = {
457
474
  warning: '{0} warning',
458
475
  warnings: '{0} warnings',
459
476
  },
477
+
478
+ carousel: {
479
+ carousel: 'carousel',
480
+ slide: 'slide',
481
+ prev: 'Previous',
482
+ next: 'Next',
483
+ position: '{0} of {1}',
484
+ },
485
+
486
+ gantt: {
487
+ title: 'Task',
488
+ start: 'Start',
489
+ end: 'End',
490
+ duration: 'Duration',
491
+ progress: 'Progress',
492
+ },
460
493
  }
461
494
  };
462
495
 
@@ -535,6 +568,22 @@ let es = {
535
568
  warning: '{0} advertencia',
536
569
  warnings: '{0} advertencias',
537
570
  },
571
+
572
+ carousel: {
573
+ carousel: 'carrusel',
574
+ slide: 'diapositiva',
575
+ prev: 'Anterior',
576
+ next: 'Siguiente',
577
+ position: '{0} de {1}',
578
+ },
579
+
580
+ gantt: {
581
+ title: 'Tarea',
582
+ start: 'Inicio',
583
+ end: 'Fin',
584
+ duration: 'Duración',
585
+ progress: 'Progreso',
586
+ },
538
587
  }
539
588
  };
540
589
 
@@ -613,6 +662,22 @@ let de = {
613
662
  warning: '{0} Warnung',
614
663
  warnings: '{0} Warnungen',
615
664
  },
665
+
666
+ carousel: {
667
+ carousel: 'Karussell',
668
+ slide: 'Folie',
669
+ prev: 'Zurück',
670
+ next: 'Weiter',
671
+ position: '{0} von {1}',
672
+ },
673
+
674
+ gantt: {
675
+ title: 'Aufgabe',
676
+ start: 'Beginn',
677
+ end: 'Ende',
678
+ duration: 'Dauer',
679
+ progress: 'Fortschritt',
680
+ },
616
681
  }
617
682
  };
618
683
 
@@ -1543,6 +1543,23 @@ export class X4PDFBuilder {
1543
1543
 
1544
1544
  return result
1545
1545
  }
1546
+
1547
+ /**
1548
+ * the document as a file, of type application/pdf (build, its streams not compressed)
1549
+ */
1550
+
1551
+ buildBlob(): Blob {
1552
+ return new Blob([this.build() as BlobPart], { type: 'application/pdf' })
1553
+ }
1554
+
1555
+ /**
1556
+ * an object url of the document: the src of an iframe, the href of a link, window.open.
1557
+ * URL.revokeObjectURL( url ) frees it when it is no longer shown
1558
+ */
1559
+
1560
+ buildURL(): string {
1561
+ return URL.createObjectURL(this.buildBlob())
1562
+ }
1546
1563
  }
1547
1564
 
1548
1565
  /*
@@ -13,25 +13,19 @@
13
13
  * Use of this source code is governed by an MIT-style license
14
14
  * that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
15
15
  *
16
- * to use you must:
17
- *
18
- * 1. add this to your tsconfig.json
16
+ * JSX in .tsx files:
17
+ *
18
+ * 1. in tsconfig.json (esbuild reads it too):
19
19
  * "compilerOptions": {
20
- * ...
21
- * "jsx": "preserve",
22
- * ...
23
- * }
24
- *
25
- * 2. be sure that esbuild has this:
26
- * jsxFactory: "x4create_element",
27
- * loader: { ".ts": "tsx" }
28
- *
29
- *
30
- * after that, all things like that will be ok
31
- *
32
- * import { x4_create_element } from "x4react"
33
- * const xx = <h1>This is a title</h1>
34
- *
20
+ * "jsx": "react",
21
+ * "jsxFactory": "x4_react.create_element"
22
+ * },
23
+ * "files": [ "node_modules/x4js/src/x4tsx.d.ts" ] // the JSX types of x4 (ref)
24
+ *
25
+ * 2. in each .tsx file, the factory imported:
26
+ * import { Label, VBox, x4_react } from "x4js";
27
+ * const box = <VBox><Label text="Hello" /></VBox>;
28
+ *
35
29
  */
36
30
 
37
31
  import { Component } from './component';
@@ -557,7 +557,8 @@ export class SvgText extends SvgItem {
557
557
  * @returns The current `SvgText` instance for chaining.
558
558
  */
559
559
  fontWeight( weight: 'light' | 'normal' | 'bold' ): this {
560
- return this.setAttr( 'font-weight', weight );
560
+ // "light" is no value of font-weight: its number
561
+ return this.setAttr( 'font-weight', weight=='light' ? '300' : weight );
561
562
  }
562
563
 
563
564
  /**
@@ -583,23 +584,25 @@ export class SvgText extends SvgItem {
583
584
  }
584
585
 
585
586
  /**
586
- * change the vertical alignment
587
+ * change the vertical alignment: y is the top, the middle, the bottom or the baseline
588
+ * of the text. dominant-baseline, read by all the browsers (alignment-baseline is not
589
+ * by Firefox before 149)
587
590
  */
588
591
  verticalAlign( align: 'top' | 'center' | 'bottom' | 'baseline' ): this {
589
592
 
590
593
  let al;
591
594
  switch( align ) {
592
595
  case 'top': al = 'hanging'; break;
593
- case 'center': al = 'middle'; break;
594
- case 'bottom': al = 'baseline'; break;
595
- case 'baseline': al = 'mathematical'; break;
596
+ case 'center': al = 'central'; break;
597
+ case 'bottom': al = 'ideographic'; break;
598
+ case 'baseline': al = 'alphabetic'; break;
596
599
  default:
597
600
  // If an invalid alignment is provided, do nothing and return this for chaining.
598
601
  console.warn(`Invalid verticalAlign value: ${align}. Must be 'top', 'center', 'bottom', or 'baseline'.`);
599
602
  return this;
600
603
  }
601
604
 
602
- return this.setAttr( 'alignment-baseline', al );
605
+ return this.setAttr( 'dominant-baseline', al );
603
606
  }
604
607
  }
605
608
 
@@ -609,6 +609,31 @@ export function formatMoney( value: number ): string {
609
609
  return numberFormat( "m" + currency, { style: "currency", currency } ).format( value );
610
610
  }
611
611
 
612
+ /**
613
+ * a percent as text in the current locale: 89 gives "89 %" in fr-FR, "89%" in en-US
614
+ * @param value - from 0 to 100, as a column percent of Gridview
615
+ * @param decimals - the most decimals shown
616
+ */
617
+
618
+ export function formatPercent( value: number, decimals = 0 ): string {
619
+ return numberFormat( "p" + decimals, { style: "percent", maximumFractionDigits: decimals } ).format( value / 100 );
620
+ }
621
+
622
+ /**
623
+ * a value and its unit, short, in the current locale: "12 j", "3 h" in fr-FR, "12 days", "3 hr" in en-US
624
+ * @param unit - a unit of Intl: "day", "hour", "minute", "kilometer"...
625
+ * @param decimals - the most decimals shown (Intl: 3 when not given)
626
+ */
627
+
628
+ export function formatUnit( value: number, unit: string, decimals?: number ): string {
629
+ const options: Intl.NumberFormatOptions = { style: "unit", unit, unitDisplay: "short" };
630
+ if( decimals!==undefined ) {
631
+ options.maximumFractionDigits = decimals;
632
+ }
633
+
634
+ return numberFormat( "u" + unit + decimals, options ).format( value );
635
+ }
636
+
612
637
  /**
613
638
  * a date as text in the current locale: "22/10/2026" in fr-FR, "10/22/2026" in en-US
614
639
  */
@@ -714,6 +739,25 @@ export function date_calc_weeknum(date: Date): number {
714
739
  return Math.ceil( ( ( d.getTime( ) - jan1 ) / 86400000 + 1 ) / 7 );
715
740
  }
716
741
 
742
+ /**
743
+ * the monday of the week of a date (the date itself on a monday), at the same time: a new date
744
+ */
745
+
746
+ export function date_week_start( date: Date ): Date {
747
+ return date_add_days( date, -( ( date.getDay( ) + 6 ) % 7 ) ); // monday 0 .. sunday 6
748
+ }
749
+
750
+ /**
751
+ * a date some days after (before when days < 0), at the same time: a new date. A day of 23 or
752
+ * 25 hours (summer time) counts as one
753
+ */
754
+
755
+ export function date_add_days( date: Date, days: number ): Date {
756
+ const result = date_clone( date );
757
+ result.setDate( result.getDate( ) + days );
758
+ return result;
759
+ }
760
+
717
761
 
718
762
 
719
763
  /**
package/src/x4doc.ts CHANGED
@@ -21,3 +21,6 @@
21
21
 
22
22
  export * from "./x4.js"
23
23
  export * from "./components/monaco/monaco.js"
24
+ export * from "./components/gantt/gantt.js"
25
+ export * from "./components/planner/planner.js"
26
+ export * from "./components/mapview/mapview.js"