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.
- package/LICENSE +21 -0
- package/README.md +5 -0
- package/aicontext.md +412 -24
- package/package.json +8 -3
- package/src/components/autocomplete/autocomplete.ts +5 -1
- package/src/components/bargraph/bargraph.module.scss +44 -0
- package/src/components/bargraph/bargraph.ts +362 -0
- package/src/components/base.scss +71 -1
- package/src/components/boxes/boxes.ts +2 -2
- package/src/components/button/button.module.scss +4 -3
- package/src/components/calendar/calendar.module.scss +6 -5
- package/src/components/calendar/calendar.ts +14 -8
- package/src/components/canvas/canvas.ts +5 -4
- package/src/components/carousel/carousel.module.scss +165 -0
- package/src/components/carousel/carousel.ts +423 -0
- package/src/components/chart/chart.module.scss +8 -0
- package/src/components/chart/chart.ts +170 -12
- package/src/components/checkbox/checkbox.module.scss +67 -1
- package/src/components/checkbox/checkbox.ts +22 -6
- package/src/components/colorinput/colorinput.module.scss +1 -0
- package/src/components/combobox/combobox.module.scss +6 -2
- package/src/components/components.ts +3 -0
- package/src/components/gantt/gantt.module.scss +462 -0
- package/src/components/gantt/gantt.ts +1254 -0
- package/src/components/gridview/gridview.module.scss +6 -0
- package/src/components/input/input.module.scss +1 -1
- package/src/components/knob/knob.module.scss +113 -0
- package/src/components/knob/knob.ts +355 -0
- package/src/components/mapview/mapview.module.scss +177 -0
- package/src/components/mapview/mapview.ts +764 -0
- package/src/components/messages/messages.module.scss +9 -4
- package/src/components/planner/planner.module.scss +688 -0
- package/src/components/planner/planner.ts +1792 -0
- package/src/components/propgrid/progrid.module.scss +13 -5
- package/src/components/radio/radio.module.scss +58 -32
- package/src/components/radio/radio.ts +12 -1
- package/src/components/sizers/sizer.ts +17 -5
- package/src/components/slider/slider.module.scss +22 -20
- package/src/components/spreadsheet/spreadsheet.module.scss +6 -0
- package/src/components/switch/switch.module.scss +68 -44
- package/src/components/switch/switch.ts +6 -3
- package/src/components/tabs/tabs.module.scss +2 -0
- package/src/components/tabs/tabs.ts +1 -0
- package/src/components/tag/tag.module.scss +4 -4
- package/src/components/textarea/textarea.module.scss +9 -0
- package/src/components/textarea/textarea.ts +1 -0
- package/src/components/textedit/textedit.module.scss +5 -2
- package/src/components/themes.scss +20 -6
- package/src/components/treeview/treeview.module.scss +6 -1
- package/src/components/treeview/treeview.ts +1 -0
- package/src/components/video/video.module.scss +5 -2
- package/src/components/video/video.ts +14 -1
- package/src/core/core_i18n.ts +65 -0
- package/src/core/core_pdf.ts +17 -0
- package/src/core/core_react.ts +12 -18
- package/src/core/core_svg.ts +9 -6
- package/src/core/core_tools.ts +44 -0
- 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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
122
|
-
--
|
|
123
|
-
|
|
124
|
-
--color-primary-
|
|
125
|
-
--color-primary-
|
|
126
|
-
--color-primary-
|
|
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;
|
|
@@ -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
|
-
|
|
28
|
-
|
|
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
|
|
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
|
}
|
package/src/core/core_i18n.ts
CHANGED
|
@@ -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
|
|
package/src/core/core_pdf.ts
CHANGED
|
@@ -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
|
/*
|
package/src/core/core_react.ts
CHANGED
|
@@ -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
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* 1.
|
|
16
|
+
* JSX in .tsx files:
|
|
17
|
+
*
|
|
18
|
+
* 1. in tsconfig.json (esbuild reads it too):
|
|
19
19
|
* "compilerOptions": {
|
|
20
|
-
*
|
|
21
|
-
* "
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* 2.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
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';
|
package/src/core/core_svg.ts
CHANGED
|
@@ -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
|
-
|
|
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 = '
|
|
594
|
-
case 'bottom': al = '
|
|
595
|
-
case 'baseline': al = '
|
|
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( '
|
|
605
|
+
return this.setAttr( 'dominant-baseline', al );
|
|
603
606
|
}
|
|
604
607
|
}
|
|
605
608
|
|
package/src/core/core_tools.ts
CHANGED
|
@@ -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
|
/**
|