x4js 2.3.28 → 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 (32) hide show
  1. package/aicontext.md +380 -19
  2. package/package.json +7 -3
  3. package/src/components/bargraph/bargraph.module.scss +44 -0
  4. package/src/components/bargraph/bargraph.ts +362 -0
  5. package/src/components/base.scss +71 -1
  6. package/src/components/calendar/calendar.module.scss +6 -5
  7. package/src/components/calendar/calendar.ts +14 -8
  8. package/src/components/canvas/canvas.ts +5 -4
  9. package/src/components/carousel/carousel.module.scss +165 -0
  10. package/src/components/carousel/carousel.ts +423 -0
  11. package/src/components/chart/chart.module.scss +8 -0
  12. package/src/components/chart/chart.ts +170 -12
  13. package/src/components/combobox/combobox.module.scss +2 -1
  14. package/src/components/components.ts +3 -0
  15. package/src/components/gantt/gantt.module.scss +462 -0
  16. package/src/components/gantt/gantt.ts +1254 -0
  17. package/src/components/knob/knob.module.scss +113 -0
  18. package/src/components/knob/knob.ts +355 -0
  19. package/src/components/mapview/mapview.module.scss +177 -0
  20. package/src/components/mapview/mapview.ts +764 -0
  21. package/src/components/planner/planner.module.scss +688 -0
  22. package/src/components/planner/planner.ts +1792 -0
  23. package/src/components/radio/radio.module.scss +4 -1
  24. package/src/components/slider/slider.module.scss +22 -20
  25. package/src/components/switch/switch.module.scss +68 -44
  26. package/src/components/switch/switch.ts +6 -3
  27. package/src/components/tag/tag.module.scss +4 -4
  28. package/src/components/textedit/textedit.module.scss +5 -2
  29. package/src/components/themes.scss +19 -6
  30. package/src/core/core_i18n.ts +65 -0
  31. package/src/core/core_tools.ts +44 -0
  32. package/src/x4doc.ts +3 -0
@@ -53,11 +53,14 @@
53
53
  flex-direction: row;
54
54
  align-items: center;
55
55
 
56
- margin-left: 5px;
56
+ // the room of a checkbox
57
+ margin: 5px;
58
+ padding: 4px;
57
59
  gap: 4px;
58
60
  cursor: pointer;
59
61
 
60
62
  .inner {
63
+ align-self: center; // a Box stretches: the round in the middle of the line
61
64
  position: relative;
62
65
  height: 1rem;
63
66
  width: 1rem;
@@ -21,15 +21,17 @@
21
21
  --slider-thumb-border-focus: var( --border-focus );
22
22
  }
23
23
 
24
+ // all in rem: the thumb, its focus, the track and the box keep their proportions whatever the
25
+ // size of <html>
24
26
  .x4slider {
25
27
  outline: none;
26
28
  touch-action: none;
27
29
  overflow: hidden;
28
30
 
29
31
  &:not(.vertical) {
30
- padding: 10px 12px;
31
- margin: 5px 0;
32
- height: 28px;
32
+ padding: 0.625rem 0.75rem;
33
+ margin: 0.3125rem 0;
34
+ height: 1.75rem;
33
35
 
34
36
  .track {
35
37
  background-color: var( --slider-track );
@@ -37,13 +39,13 @@
37
39
 
38
40
  position: relative;
39
41
  top: 50%;
40
- height: 3px;
42
+ height: 0.1875rem;
41
43
  }
42
44
 
43
45
  .bar {
44
46
  position: absolute;
45
47
  width: 61%;
46
- height: 3px;
48
+ height: 0.1875rem;
47
49
  //padding: 4px 0;
48
50
  //background-color: var( --color-60 );
49
51
  }
@@ -54,12 +56,12 @@
54
56
  outline: none;
55
57
 
56
58
  background-color: var( --slider-thumb-background );
57
- border: 3px solid var( --slider-thumb-border );
58
- border-radius: 8px;
59
+ border: 0.1875rem solid var( --slider-thumb-border );
60
+ border-radius: 50%;
59
61
 
60
62
  top: 50%;
61
- width: 16px;
62
- height: 16px;
63
+ width: 1rem;
64
+ height: 1rem;
63
65
 
64
66
  transform: translate(-50%,-50%);
65
67
  cursor: pointer;
@@ -67,9 +69,9 @@
67
69
  }
68
70
 
69
71
  &.vertical {
70
- padding: 12px 10px;
71
- margin: 0 5px;
72
- width: 28px;
72
+ padding: 0.75rem 0.625rem;
73
+ margin: 0 0.3125rem;
74
+ width: 1.75rem;
73
75
 
74
76
  .track {
75
77
  background-color: var( --slider-track );
@@ -77,12 +79,12 @@
77
79
 
78
80
  position: relative;
79
81
  left: 50%;
80
- width: 3px;
82
+ width: 0.1875rem;
81
83
  }
82
84
 
83
85
  .bar {
84
86
  position: absolute;
85
- width: 3px;
87
+ width: 0.1875rem;
86
88
  height: 61%;
87
89
  //padding: 4px 0;
88
90
  //background-color: var( --color-60 );
@@ -94,12 +96,12 @@
94
96
  outline: none;
95
97
 
96
98
  background-color: var( --slider-thumb-background );
97
- border: 3px solid var( --slider-thumb-border );
98
- border-radius: 8px;
99
+ border: 0.1875rem solid var( --slider-thumb-border );
100
+ border-radius: 50%;
99
101
 
100
102
  top: 61%;
101
- width: 16px;
102
- height: 16px;
103
+ width: 1rem;
104
+ height: 1rem;
103
105
 
104
106
  transform: translate(-50%,-50%);
105
107
  cursor: pointer;
@@ -110,9 +112,9 @@
110
112
 
111
113
 
112
114
  &:focus .thumb {
113
- outline-offset: 3px;
115
+ outline-offset: 0.1875rem;
114
116
  outline-color: var(--slider-thumb-border-focus);
115
117
  outline-style: solid;
116
- outline-width: 1px;
118
+ outline-width: 0.0625rem;
117
119
  }
118
120
  }
@@ -1,16 +1,16 @@
1
- /**
1
+ /**
2
2
  * ___ ___ __
3
3
  * \ \/ / / _
4
4
  * \ / /_| |_
5
- * / \____ _|
5
+ * / \____ _|
6
6
  * /__/\__\ |_|
7
- *
7
+ *
8
8
  * @file switch.module.scss
9
- * @author Etienne Cochard
10
- *
9
+ * @author Etienne Cochard
10
+ *
11
11
  * @copyright (c) 2026 R-libre ingenierie
12
12
  *
13
- * Use of this source code is governed by an MIT-style license
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
16
 
@@ -21,15 +21,21 @@
21
21
  --switch-background: var( --background-hover );
22
22
  --switch-background-disabled: var( --disabled-background );
23
23
  --switch-checked: var( --accent-background );
24
+ --switch-danger: var( --alert-background );
24
25
  --switch-thumb: var( --background-primary );
25
26
  }
26
27
 
28
+ // The sizes come from H, the height of the track: 1.125rem rounded to an even number of pixels
29
+ // (18px when <html> has 16px). The width is 2H - 2, the thumb H - 2 at 1px of the edges (inside
30
+ // the border): all in whole pixels, the switch is drawn the same wherever it falls in the page.
31
+ // The px values first: a browser without round() keeps them.
32
+
27
33
  .x4switch {
28
34
  display: flex;
29
35
  flex-direction: row;
30
36
  align-items: center;
31
37
  position: relative;
32
-
38
+
33
39
  gap: 4px;
34
40
  margin-left: 5px;
35
41
  padding: 4px;
@@ -47,62 +53,79 @@
47
53
  }
48
54
  }
49
55
 
56
+ // the track; the input over it takes the click, the focus, and the value for a Form
50
57
  .switch {
51
- width: 2.2rem;
52
- height: 18px;
53
58
  position: relative;
59
+ flex: none;
60
+
61
+ width: 34px;
62
+ width: calc( round( 1.125rem, 2px ) * 2 - 2px );
63
+ height: 18px;
64
+ height: round( 1.125rem, 2px );
65
+
66
+ border: 1px solid var( --switch-border );
67
+ border-radius: 9px;
68
+ border-radius: calc( round( 1.125rem, 2px ) / 2 );
69
+ background-color: var( --switch-background );
70
+
71
+ transition: background-color ease 0.2s, border-color ease 0.2s;
54
72
 
55
73
  input {
56
74
  position: absolute;
57
- width: 100%;
58
- height: 100%;
59
- appearance: none;
60
- padding: 0;
61
- margin: 0;
62
- left: 0;
63
- top: 0;
64
- cursor: pointer;
65
- }
66
-
67
- .track {
68
- position: absolute;
69
- pointer-events: none;
70
75
  left: 0;
71
76
  top: 0;
72
77
  width: 100%;
73
78
  height: 100%;
74
- border: 1px solid var( --switch-border );
75
-
76
- background-color: var( --switch-background );
77
- border-radius: 9px;
78
-
79
- transition: background-color ease 0.2s;
79
+ margin: 0;
80
+ padding: 0;
81
+ appearance: none;
82
+ opacity: 0; // none of the look of a field (its background): the track is under it
83
+ cursor: pointer;
80
84
  }
81
85
 
82
86
  .thumb {
83
87
  position: absolute;
84
88
  pointer-events: none;
85
- left: 0.05rem;
86
- top: 1px;
89
+ left: 0;
90
+ top: 0;
87
91
 
88
- width: 1rem;
89
- height: 1rem;
92
+ width: 16px;
93
+ width: calc( round( 1.125rem, 2px ) - 2px );
94
+ height: 16px;
95
+ height: calc( round( 1.125rem, 2px ) - 2px );
90
96
 
91
- border-radius: 8px;
97
+ border-radius: 50%;
92
98
  background-color: var( --switch-thumb );
93
99
  box-shadow: 0 0 2px rgba(0,0,0,0.5);
94
100
 
95
- transition: left ease 0.2s;
101
+ transition: transform ease 0.2s;
96
102
  }
97
103
  }
98
104
 
99
- &:has(input:checked) {
100
- .switch .thumb {
101
- left: 1.15rem;
105
+ // on: the colour on all the track, its border too; the thumb at the other end (H - 2 further)
106
+ &:has(input:checked) .switch {
107
+ background-color: var( --switch-checked );
108
+ border-color: var( --switch-checked );
109
+
110
+ .thumb {
111
+ transform: translateX( 16px );
112
+ transform: translateX( calc( round( 1.125rem, 2px ) - 2px ) );
102
113
  }
103
- .switch .track {
104
- background-color: var( --switch-checked );
105
- //border-color: var( --color-70 );
114
+ }
115
+
116
+ // danger: the colour of an alert when on
117
+ &.danger {
118
+ --switch-checked: var( --switch-danger );
119
+ }
120
+
121
+ // square: the corners of the track and of the thumb barely rounded
122
+ &.square .switch {
123
+ border-radius: 3px;
124
+ border-radius: round( 0.1875rem, 1px );
125
+
126
+ .thumb {
127
+ border-radius: 2px;
128
+ border-radius: round( 0.125rem, 1px );
106
129
  }
107
130
  }
108
131
 
@@ -110,7 +133,7 @@
110
133
  &, * {
111
134
  cursor: not-allowed;
112
135
  }
113
-
136
+
114
137
  input {
115
138
  pointer-events: none;
116
139
  }
@@ -120,9 +143,10 @@
120
143
  color: var( --checkbox-color-disabled );
121
144
  }
122
145
 
123
- .switch .track,
124
- &.x4switch:has(input:checked) .switch .track {
146
+ .switch,
147
+ &.x4switch:has(input:checked) .switch {
125
148
  background-color: var( --switch-background-disabled );
149
+ border-color: var( --switch-border );
126
150
  }
127
151
  }
128
- }
152
+ }
@@ -31,13 +31,16 @@ interface SwitchProps extends ComponentProps {
31
31
  }
32
32
 
33
33
  /**
34
- *
34
+ * cls "square": the track and the thumb with corners barely rounded; "danger": the colour of an
35
+ * alert when on.
36
+ *
35
37
  * @cssvar
36
38
  * ```
37
39
  * --switch-border
38
40
  * --switch-background
39
41
  * --switch-background-disabled
40
42
  * --switch-checked
43
+ * --switch-danger
41
44
  * --switch-thumb
42
45
  * ```
43
46
  */
@@ -51,12 +54,12 @@ export class Switch extends HBox<SwitchProps> {
51
54
  const inputId = makeUniqueComponentId( );
52
55
 
53
56
  this.setContent( [
57
+ // the track, its thumb in it; the input over them (role switch: a screen reader says "switch, on")
54
58
  new Component( {
55
59
  cls: "switch",
56
60
  content: [
57
61
  // value: sent by a <form> of the browser when checked; a Form of x4 reads a boolean
58
- new Input( { type: "checkbox", id: inputId, name: props.name, value: props.value, checked: props.checked } ),
59
- new Component( { cls: "track" } ),
62
+ new Input( { type: "checkbox", id: inputId, name: props.name, value: props.value, checked: props.checked, attrs: { role: "switch" } } ),
60
63
  new Component( { cls: "thumb" } ),
61
64
  ]
62
65
  } ),
@@ -19,8 +19,7 @@
19
19
  --tag-border: var( --border );
20
20
  --tag-border-focus: var( --border-focus );
21
21
  --tag-background: var( --accent-background );
22
- --tag-color: var( --text-primary );
23
- --tag-icon-color: var( --tag-color );
22
+ --tag-color: var( --accent-color );
24
23
  }
25
24
 
26
25
  .x4tag {
@@ -37,9 +36,10 @@
37
36
  display: none;
38
37
  }
39
38
 
39
+ // --tag-icon-color is not set by x4: the icon has the color of the text of its tag
40
40
  .x4icon {
41
41
  height: 1em;
42
- color: var( --tag-icon-color );
42
+ color: var( --tag-icon-color, var( --tag-color ) );
43
43
  }
44
44
 
45
45
  &> .x4simpletext {
@@ -50,6 +50,6 @@
50
50
 
51
51
  &.alert {
52
52
  --tag-color: var( --alert-color );
53
- background-color: var( --alert-background );
53
+ --tag-background: var( --alert-background );
54
54
  }
55
55
  }
@@ -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%)};
@@ -119,12 +122,16 @@ html.x4theme-dark {
119
122
  --color-primary-a10: #2a2d2e; // list.hoverBackground
120
123
  --color-primary-a20: #37373d; // list.inactiveSelectionBackground
121
124
  --color-primary-a30: #3c3c3c; // input.border
122
- --color-primary-a40: #04395e; // list.activeSelectionBackground
123
- --color-primary-a50: #0078d4; // button.background, focusBorder
124
- --color-primary-a55: #026ec1; // button.hoverBackground
125
- --color-primary-a60: #0078d4;
126
- --color-primary-a70: #026ec1;
127
- --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
+
128
135
  --color-primary-a90: #cccccc;
129
136
  --color-primary-a100: #cccccc; // foreground
130
137
 
@@ -156,5 +163,11 @@ html.x4theme-dark {
156
163
  --color-dialog-caption-text: #cccccc; // titleBar.activeForeground
157
164
  --color-dialog-background: #202020; // editorWidget.background
158
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
+
159
172
  color-scheme: dark; // the native parts: select, scroll bars, number spinners
160
173
  }
@@ -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
 
@@ -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"