x4js 2.3.25 → 2.3.27
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/aicontext.md +87 -13
- package/package.json +1 -1
- package/src/components/autocomplete/autocomplete.ts +3 -3
- package/src/components/boxes/boxes.module.scss +16 -2
- package/src/components/boxes/boxes.ts +24 -11
- package/src/components/breadcrumb/breadcrumb.ts +1 -2
- package/src/components/btngroup/btngroup.ts +2 -2
- package/src/components/button/button.ts +2 -2
- package/src/components/calendar/calendar.ts +5 -3
- package/src/components/canvas/canvas.ts +1 -2
- package/src/components/chart/chart.ts +4 -4
- package/src/components/checkbox/checkbox.ts +2 -2
- package/src/components/colorinput/colorinput.ts +3 -2
- package/src/components/colorpicker/colorpicker.ts +5 -5
- package/src/components/combobox/combobox.ts +3 -3
- package/src/components/components.ts +1 -0
- package/src/components/dialog/dialog.ts +2 -2
- package/src/components/dockingview/dockingview.ts +2 -3
- package/src/components/filedrop/filedrop.ts +3 -2
- package/src/components/form/form.ts +4 -3
- package/src/components/gauge/gauge.ts +2 -2
- package/src/components/gridview/gridview.module.scss +8 -5
- package/src/components/gridview/gridview.ts +11 -13
- package/src/components/header/header.ts +2 -2
- package/src/components/icon/icon.ts +1 -2
- package/src/components/image/image.ts +1 -2
- package/src/components/input/input.ts +3 -2
- package/src/components/keyboard/keyboard.ts +2 -2
- package/src/components/label/label.ts +3 -3
- package/src/components/link/link.ts +3 -2
- package/src/components/listbox/listbox.ts +33 -47
- package/src/components/menu/menu.ts +5 -4
- package/src/components/messages/messages.module.scss +29 -5
- package/src/components/messages/messages.ts +42 -50
- package/src/components/monaco/monaco.ts +1 -2
- package/src/components/notification/notification.ts +3 -2
- package/src/components/panel/panel.ts +2 -2
- package/src/components/popup/popup.ts +4 -4
- package/src/components/progress/progress.ts +1 -2
- package/src/components/propgrid/progrid.module.scss +2 -3
- package/src/components/propgrid/propgrid.ts +2 -2
- package/src/components/radio/radio.ts +1 -2
- package/src/components/rating/rating.ts +3 -3
- package/src/components/select/select.ts +2 -2
- package/src/components/settingsview/settingsview.module.scss +6 -5
- package/src/components/settingsview/settingsview.ts +3 -3
- package/src/components/sizers/sizer.ts +21 -29
- package/src/components/slider/slider.ts +2 -2
- package/src/components/spreadsheet/spreadsheet.ts +8 -10
- package/src/components/switch/switch.ts +2 -2
- package/src/components/tabs/tabs.ts +4 -4
- package/src/components/tag/tag.ts +2 -2
- package/src/components/textarea/textarea.ts +3 -3
- package/src/components/textedit/textedit.ts +2 -2
- package/src/components/themes.scss +2 -0
- package/src/components/tickline/tickline.ts +3 -3
- package/src/components/timeline/timeline.module.scss +147 -0
- package/src/components/timeline/timeline.ts +146 -0
- package/src/components/tooltips/tooltips.ts +2 -2
- package/src/components/treeview/treeview.ts +19 -25
- package/src/components/video/video.ts +1 -2
- package/src/components/viewport/viewport.ts +4 -3
- package/src/core/component.ts +46 -26
- package/src/core/core_application.ts +29 -1
- package/src/core/core_i18n.ts +39 -0
- package/src/core/core_shortcuts.ts +2 -1
- package/src/core/core_svg.ts +1 -0
- package/src/core/core_tools.ts +83 -24
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { Component, ComponentContent, ComponentEvent, ComponentEvents, ComponentProps, EvChange, EvClick, EvContextMenu, EvDblClick, EvSelectionChange, componentFromDOM } from '../../core/component';
|
|
19
19
|
import { GridColumn } from '../gridview/gridview'
|
|
20
20
|
|
|
21
|
-
import {
|
|
21
|
+
import { formatDate, formatMoney, formatNumber, IFormElement, isFunction, isNumber, isString, UnsafeHtml } from '../../core/core_tools';
|
|
22
22
|
import { CoreEvent, EventCallback, EventMap } from '../../core/core_events';
|
|
23
23
|
import { kbNav } from '../../core/core_tools';
|
|
24
24
|
|
|
@@ -282,8 +282,8 @@ export interface SpreadsheetProps extends ComponentProps {
|
|
|
282
282
|
* ```
|
|
283
283
|
*/
|
|
284
284
|
|
|
285
|
-
@class_ns("x4")
|
|
286
285
|
export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extends SpreadsheetEvents = SpreadsheetEvents> extends Component<P, E> {
|
|
286
|
+
static classPrefix = "x4";
|
|
287
287
|
|
|
288
288
|
private _columns: SpreadsheetColumn[];
|
|
289
289
|
private _store: Store;
|
|
@@ -310,9 +310,6 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
310
310
|
private _end: number;
|
|
311
311
|
|
|
312
312
|
private _selection: Set<number>;
|
|
313
|
-
private _num_fmt = new Intl.NumberFormat('fr-FR');
|
|
314
|
-
private _mny_fmt = new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' });
|
|
315
|
-
private _dte_fmt = new Intl.DateTimeFormat('fr-FR', {});
|
|
316
313
|
|
|
317
314
|
private _has_fixed: boolean;
|
|
318
315
|
private _has_footer: boolean;
|
|
@@ -821,12 +818,13 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
821
818
|
})
|
|
822
819
|
|
|
823
820
|
sizer.on("resize", (ev) => {
|
|
824
|
-
|
|
821
|
+
const size = Math.max( ev.size, 16 ); // 16px at least: the min-width of a .cell
|
|
822
|
+
cdata.width = size;
|
|
825
823
|
cdata.flex = 0;
|
|
826
824
|
|
|
827
825
|
const cols = this.queryAll(`[data-col="${col}"]`)
|
|
828
826
|
cols.forEach(c => {
|
|
829
|
-
c.setStyleValue("width",
|
|
827
|
+
c.setStyleValue("width", size + "px");
|
|
830
828
|
});
|
|
831
829
|
|
|
832
830
|
const rh = header.getBoundingRect();
|
|
@@ -990,7 +988,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
990
988
|
return "NaN";
|
|
991
989
|
}
|
|
992
990
|
|
|
993
|
-
data =
|
|
991
|
+
data = formatNumber(data as number);
|
|
994
992
|
break;
|
|
995
993
|
}
|
|
996
994
|
|
|
@@ -999,7 +997,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
999
997
|
return "NaN";
|
|
1000
998
|
}
|
|
1001
999
|
|
|
1002
|
-
data =
|
|
1000
|
+
data = formatMoney(data as number);
|
|
1003
1001
|
break;
|
|
1004
1002
|
}
|
|
1005
1003
|
|
|
@@ -1015,7 +1013,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
1015
1013
|
}
|
|
1016
1014
|
|
|
1017
1015
|
case "date": {
|
|
1018
|
-
data =
|
|
1016
|
+
data = formatDate(data as Date);
|
|
1019
1017
|
break;
|
|
1020
1018
|
}
|
|
1021
1019
|
|
|
@@ -22,7 +22,6 @@ import { Label } from '../label/label';
|
|
|
22
22
|
import { HBox } from '../boxes/boxes';
|
|
23
23
|
|
|
24
24
|
import "./switch.module.scss";
|
|
25
|
-
import { class_ns } from '../../core/core_tools';
|
|
26
25
|
|
|
27
26
|
interface SwitchProps extends ComponentProps {
|
|
28
27
|
label: string;
|
|
@@ -43,8 +42,9 @@ interface SwitchProps extends ComponentProps {
|
|
|
43
42
|
* ```
|
|
44
43
|
*/
|
|
45
44
|
|
|
46
|
-
@class_ns( "x4" )
|
|
47
45
|
export class Switch extends HBox<SwitchProps> {
|
|
46
|
+
static classPrefix = "x4";
|
|
47
|
+
|
|
48
48
|
constructor(props: SwitchProps ) {
|
|
49
49
|
super( props );
|
|
50
50
|
|
|
@@ -20,7 +20,6 @@ import { Button, ButtonProps } from '../button/button';
|
|
|
20
20
|
import { HBox, VBox, StackBox, Box } from '../boxes/boxes';
|
|
21
21
|
|
|
22
22
|
import "./tabs.module.scss"
|
|
23
|
-
import { class_ns } from '../../core/core_tools';
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
25
|
*
|
|
@@ -40,8 +39,9 @@ export interface TabItem {
|
|
|
40
39
|
*
|
|
41
40
|
*/
|
|
42
41
|
|
|
43
|
-
@class_ns( "x4" )
|
|
44
42
|
class CTab extends Button {
|
|
43
|
+
static classPrefix = "x4";
|
|
44
|
+
|
|
45
45
|
constructor( props: ButtonProps, item: TabItem ) {
|
|
46
46
|
super( props );
|
|
47
47
|
|
|
@@ -78,8 +78,8 @@ interface TablistEvents extends ComponentEvents {
|
|
|
78
78
|
* @internal
|
|
79
79
|
*/
|
|
80
80
|
|
|
81
|
-
@class_ns( "x4" )
|
|
82
81
|
class CTabList extends HBox<TablistProps,TablistEvents> {
|
|
82
|
+
static classPrefix = "x4";
|
|
83
83
|
|
|
84
84
|
private _selitem: Button;
|
|
85
85
|
|
|
@@ -152,8 +152,8 @@ interface TabsProps extends Omit<ComponentProps,"content"> {
|
|
|
152
152
|
* ```
|
|
153
153
|
*/
|
|
154
154
|
|
|
155
|
-
@class_ns( "x4" )
|
|
156
155
|
export class Tabs extends Box<TabsProps> {
|
|
156
|
+
static classPrefix = "x4";
|
|
157
157
|
|
|
158
158
|
private _list: CTabList;
|
|
159
159
|
private _stack: StackBox;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
import { ComponentProps } from '../../core/component';
|
|
19
|
-
import {
|
|
19
|
+
import { UnsafeHtml } from '../../core/core_tools';
|
|
20
20
|
import { HBox } from '../boxes/boxes';
|
|
21
21
|
import { Icon } from '../icon/icon';
|
|
22
22
|
import { SimpleText } from '../label/label';
|
|
@@ -40,8 +40,8 @@ export interface TagProps extends ComponentProps {
|
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
42
|
|
|
43
|
-
@class_ns( "x4" )
|
|
44
43
|
export class Tag extends HBox<TagProps> {
|
|
44
|
+
static classPrefix = "x4";
|
|
45
45
|
|
|
46
46
|
constructor( props: TagProps ) {
|
|
47
47
|
super( props );
|
|
@@ -23,7 +23,7 @@ import { Label } from '../label/label';
|
|
|
23
23
|
import { VBox } from '../boxes/boxes';
|
|
24
24
|
|
|
25
25
|
import "./textarea.module.scss";
|
|
26
|
-
import {
|
|
26
|
+
import { IFormElement } from '../../core/core_tools';
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
*
|
|
@@ -50,8 +50,8 @@ export interface TextAreaEvents extends ComponentEvents {
|
|
|
50
50
|
*
|
|
51
51
|
*/
|
|
52
52
|
|
|
53
|
-
@class_ns( "x4" )
|
|
54
53
|
export class SimpleTextArea extends Component<SimpleTextAreaProps,TextAreaEvents> {
|
|
54
|
+
static classPrefix = "x4";
|
|
55
55
|
|
|
56
56
|
constructor( props: SimpleTextAreaProps ) {
|
|
57
57
|
super( { ...props, tag: "textarea" } );
|
|
@@ -142,8 +142,8 @@ export class SimpleTextArea extends Component<SimpleTextAreaProps,TextAreaEvents
|
|
|
142
142
|
* ```
|
|
143
143
|
*/
|
|
144
144
|
|
|
145
|
-
@class_ns( "x4" )
|
|
146
145
|
export class TextArea extends VBox {
|
|
146
|
+
static classPrefix = "x4";
|
|
147
147
|
|
|
148
148
|
private _input: SimpleTextArea;
|
|
149
149
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
//import { EventCallback } from '../../core/core_events';
|
|
18
18
|
import { Component, ComponentEvents, makeUniqueComponentId } from '../../core/component';
|
|
19
|
-
import {
|
|
19
|
+
import { UnsafeHtml } from '../../core/core_tools';
|
|
20
20
|
|
|
21
21
|
import { BoxProps, HBox } from '../boxes/boxes';
|
|
22
22
|
import { DateProps, Input, InputProps, NumberProps, TextInputProps, TimeProps } from "../input/input"
|
|
@@ -81,8 +81,8 @@ interface TextEditProps extends InputProps {
|
|
|
81
81
|
* ```
|
|
82
82
|
*/
|
|
83
83
|
|
|
84
|
-
@class_ns( "x4" )
|
|
85
84
|
export class TextEdit<E extends ComponentEvents = ComponentEvents> extends HBox<BoxProps,E> {
|
|
85
|
+
static classPrefix = "x4";
|
|
86
86
|
|
|
87
87
|
private input: Input;
|
|
88
88
|
|
|
@@ -92,9 +92,11 @@ $color--interval: 8.4%;
|
|
|
92
92
|
// the red, orange and green do not follow the scale: their text stays white
|
|
93
93
|
--alert-background: var(--color-danger-a50);
|
|
94
94
|
--alert-background-active: var(--color-danger-a60);
|
|
95
|
+
--alert-background-light: color-mix(in srgb, var(--alert-background) 15%, transparent); // the red at 15%
|
|
95
96
|
--alert-color: #ffffff;
|
|
96
97
|
|
|
97
98
|
--warning-background: var(--color-warning-a50);
|
|
99
|
+
--warning-background-light: color-mix(in srgb, var(--warning-background) 15%, transparent); // the orange at 15%
|
|
98
100
|
--warning-color: #ffffff;
|
|
99
101
|
|
|
100
102
|
--success-background: var(--color-success-a50);
|
|
@@ -3,7 +3,7 @@ import { Color } from '../../core/core_colors';
|
|
|
3
3
|
import { Component, ComponentProps } from '../../core/component';
|
|
4
4
|
|
|
5
5
|
import "./tickline.module.scss"
|
|
6
|
-
import {
|
|
6
|
+
import { formatNumber } from '../../core/core_tools';
|
|
7
7
|
|
|
8
8
|
export interface TickLineProps extends ComponentProps {
|
|
9
9
|
values: number[];
|
|
@@ -31,8 +31,8 @@ export interface TickLineProps extends ComponentProps {
|
|
|
31
31
|
*
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
|
-
@class_ns( "x4" )
|
|
35
34
|
export class TickLine extends Component<TickLineProps> {
|
|
35
|
+
static classPrefix = "x4";
|
|
36
36
|
|
|
37
37
|
constructor( props: TickLineProps ) {
|
|
38
38
|
super( props );
|
|
@@ -102,7 +102,7 @@ export class TickLine extends Component<TickLineProps> {
|
|
|
102
102
|
.antiAlias( false );
|
|
103
103
|
|
|
104
104
|
if( props.display?.tooltips ) {
|
|
105
|
-
r.setAttr( 'tooltip', vals[x]
|
|
105
|
+
r.setAttr( 'tooltip', formatNumber( vals[x], 1 ) )
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ___ ___ __
|
|
3
|
+
* \ \/ / / _
|
|
4
|
+
* \ / /_| |_
|
|
5
|
+
* / \____ _|
|
|
6
|
+
* /__/\__\ |_|
|
|
7
|
+
*
|
|
8
|
+
* @file timeline.module.scss
|
|
9
|
+
* @author Etienne Cochard
|
|
10
|
+
*
|
|
11
|
+
* @copyright (c) 2026 R-libre ingenierie
|
|
12
|
+
*
|
|
13
|
+
* Use of this source code is governed by an MIT-style license
|
|
14
|
+
* that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
|
|
15
|
+
**/
|
|
16
|
+
|
|
17
|
+
:root {
|
|
18
|
+
--timeline-done-color: var( --accent-background );
|
|
19
|
+
--timeline-done-icon-color: var( --accent-color );
|
|
20
|
+
--timeline-todo-color: var( --border );
|
|
21
|
+
--timeline-todo-icon-color: var( --text-ternary );
|
|
22
|
+
--timeline-title-color: var( --text-primary );
|
|
23
|
+
--timeline-text-color: var( --text-ternary );
|
|
24
|
+
--timeline-time-color: var( --text-primary );
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// side by side: the marks on a line at the top, the content under each one
|
|
28
|
+
|
|
29
|
+
.x4timeline {
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: row;
|
|
32
|
+
|
|
33
|
+
.item {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
flex: 1 1 0;
|
|
37
|
+
min-width: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.rail {
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-direction: row;
|
|
43
|
+
align-items: center;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.mark {
|
|
47
|
+
display: flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
justify-content: center;
|
|
50
|
+
flex-shrink: 0;
|
|
51
|
+
width: 24px;
|
|
52
|
+
height: 24px;
|
|
53
|
+
border-radius: 50%;
|
|
54
|
+
background-color: var( --timeline-todo-color );
|
|
55
|
+
|
|
56
|
+
.x4icon {
|
|
57
|
+
height: 12px;
|
|
58
|
+
color: var( --timeline-todo-icon-color );
|
|
59
|
+
fill: var( --timeline-todo-icon-color );
|
|
60
|
+
|
|
61
|
+
&.empty {
|
|
62
|
+
display: none;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// the line to the next mark: only one side has a width
|
|
68
|
+
.line {
|
|
69
|
+
flex: 1;
|
|
70
|
+
border: 0 dashed var( --timeline-todo-color );
|
|
71
|
+
border-top-width: 2px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.item.done > .rail {
|
|
75
|
+
.mark {
|
|
76
|
+
background-color: var( --timeline-done-color );
|
|
77
|
+
|
|
78
|
+
.x4icon {
|
|
79
|
+
color: var( --timeline-done-icon-color );
|
|
80
|
+
fill: var( --timeline-done-icon-color );
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.line {
|
|
85
|
+
border-style: solid;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.item.next-done > .rail > .line {
|
|
90
|
+
border-color: var( --timeline-done-color );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.item:last-child > .rail > .line {
|
|
94
|
+
display: none;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.content {
|
|
98
|
+
padding: 8px 12px 0 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.title {
|
|
102
|
+
font-weight: bold;
|
|
103
|
+
color: var( --timeline-title-color );
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.text {
|
|
107
|
+
color: var( --timeline-text-color );
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.time {
|
|
111
|
+
margin-top: 4px;
|
|
112
|
+
font-size: 0.85em;
|
|
113
|
+
font-weight: 500;
|
|
114
|
+
color: var( --timeline-time-color );
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// one under the other: the marks on a line on the left, the content beside each one
|
|
118
|
+
|
|
119
|
+
&.vertical {
|
|
120
|
+
flex-direction: column;
|
|
121
|
+
|
|
122
|
+
.item {
|
|
123
|
+
flex-direction: row;
|
|
124
|
+
flex: 0 0 auto;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.rail {
|
|
128
|
+
flex-direction: column;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.line {
|
|
132
|
+
border-top-width: 0;
|
|
133
|
+
border-left-width: 2px;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.content {
|
|
137
|
+
flex: 1;
|
|
138
|
+
min-width: 0;
|
|
139
|
+
padding: 0 0 1.5em 12px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// the title on the middle of the mark
|
|
143
|
+
.title {
|
|
144
|
+
line-height: 24px;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ___ ___ __
|
|
3
|
+
* \ \/ / / _
|
|
4
|
+
* \ / /_| |_
|
|
5
|
+
* / \____ _|
|
|
6
|
+
* /__/\__\ |_|
|
|
7
|
+
*
|
|
8
|
+
* @file timeline.ts
|
|
9
|
+
* @author Etienne Cochard
|
|
10
|
+
*
|
|
11
|
+
* @copyright (c) 2026 R-libre ingenierie
|
|
12
|
+
*
|
|
13
|
+
* Use of this source code is governed by an MIT-style license
|
|
14
|
+
* that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
|
|
15
|
+
**/
|
|
16
|
+
|
|
17
|
+
import { Component, ComponentEvents, ComponentProps, EvClick } from '../../core/component';
|
|
18
|
+
import { EventCallback } from '../../core/core_events';
|
|
19
|
+
import { UnsafeHtml } from '../../core/core_tools';
|
|
20
|
+
import { getCurrentLocale } from '../../core/core_i18n';
|
|
21
|
+
import { Icon } from '../icon/icon';
|
|
22
|
+
|
|
23
|
+
import "./timeline.module.scss";
|
|
24
|
+
|
|
25
|
+
export interface TimelineItem {
|
|
26
|
+
icon?: string; // in the round mark
|
|
27
|
+
title: string | UnsafeHtml; // in bold
|
|
28
|
+
text?: string | UnsafeHtml; // the sentence under it
|
|
29
|
+
date?: Date; // written relative to now, in the locale: "2 hours ago"; none: no time shown
|
|
30
|
+
done?: boolean; // the mark filled in the accent; else a grey mark
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface TimelineEvents extends ComponentEvents {
|
|
34
|
+
click: EvClick; // context: the TimelineItem clicked
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface TimelineProps extends ComponentProps {
|
|
38
|
+
items?: TimelineItem[];
|
|
39
|
+
vertical?: boolean; // true by default; false: the events side by side
|
|
40
|
+
renderer?: ( item: TimelineItem ) => Component; // what is beside the mark
|
|
41
|
+
click?: EventCallback<EvClick>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* "2 hours ago", "yesterday", "in 3 days": the largest unit that gives at least 1
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
function relativeTime( date: Date ): string {
|
|
49
|
+
const fmt = new Intl.RelativeTimeFormat( getCurrentLocale( ), { numeric: "auto" } );
|
|
50
|
+
const sec = ( date.getTime( ) - Date.now( ) ) / 1000; // < 0: in the past
|
|
51
|
+
const abs = Math.abs( sec );
|
|
52
|
+
|
|
53
|
+
if( abs < 60 ) {
|
|
54
|
+
return fmt.format( Math.trunc( sec ), "second" );
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if( abs < 3600 ) {
|
|
58
|
+
return fmt.format( Math.trunc( sec / 60 ), "minute" );
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if( abs < 86400 ) {
|
|
62
|
+
return fmt.format( Math.trunc( sec / 3600 ), "hour" );
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if( abs < 86400 * 30 ) {
|
|
66
|
+
return fmt.format( Math.trunc( sec / 86400 ), "day" );
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if( abs < 86400 * 365 ) {
|
|
70
|
+
return fmt.format( Math.trunc( sec / ( 86400 * 30 ) ), "month" );
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return fmt.format( Math.trunc( sec / ( 86400 * 365 ) ), "year" );
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Events one after the other on a line, each with its icon in a round mark.
|
|
78
|
+
* The line from a mark to the next one is in the accent when the next event is done,
|
|
79
|
+
* dashed when the event it leaves is not done.
|
|
80
|
+
*
|
|
81
|
+
* @cssvar
|
|
82
|
+
* ```
|
|
83
|
+
* --timeline-done-color
|
|
84
|
+
* --timeline-done-icon-color
|
|
85
|
+
* --timeline-todo-color
|
|
86
|
+
* --timeline-todo-icon-color
|
|
87
|
+
* --timeline-title-color
|
|
88
|
+
* --timeline-text-color
|
|
89
|
+
* --timeline-time-color
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
export class Timeline extends Component<TimelineProps, TimelineEvents> {
|
|
94
|
+
static classPrefix = "x4";
|
|
95
|
+
|
|
96
|
+
constructor( props: TimelineProps ) {
|
|
97
|
+
super( props );
|
|
98
|
+
|
|
99
|
+
this.setClass( "vertical", props.vertical ?? true );
|
|
100
|
+
|
|
101
|
+
this.mapPropEvents( props, "click" );
|
|
102
|
+
this.addDOMEvent( "click", ( ev ) => this._onClick( ev ) );
|
|
103
|
+
|
|
104
|
+
if( props.items ) {
|
|
105
|
+
this.setItems( props.items );
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
setItems( items: TimelineItem[] ) {
|
|
110
|
+
const renderer = this.props.renderer;
|
|
111
|
+
|
|
112
|
+
const content = items.map( ( item, i ) => {
|
|
113
|
+
const body = renderer ? renderer( item ) : [
|
|
114
|
+
new Component( { cls: "title", content: item.title } ),
|
|
115
|
+
item.text ? new Component( { cls: "text", content: item.text } ) : null,
|
|
116
|
+
item.date ? new Component( { cls: "time", content: relativeTime( item.date ) } ) : null,
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
const el = new Component( { cls: "item", content: [
|
|
120
|
+
new Component( { cls: "rail", content: [
|
|
121
|
+
new Component( { cls: "mark", content: new Icon( { iconId: item.icon } ) } ),
|
|
122
|
+
new Component( { cls: "line" } ),
|
|
123
|
+
] } ),
|
|
124
|
+
new Component( { cls: "content", content: body } ),
|
|
125
|
+
] } );
|
|
126
|
+
|
|
127
|
+
el.setClass( "done", !!item.done );
|
|
128
|
+
el.setClass( "next-done", i + 1 < items.length && !!items[i + 1].done );
|
|
129
|
+
el.setInternalData( "item", item );
|
|
130
|
+
return el;
|
|
131
|
+
} );
|
|
132
|
+
|
|
133
|
+
this.setContent( content );
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* a click anywhere in an event: its item
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
private _onClick( ev: MouseEvent ) {
|
|
141
|
+
const el = Component.closest( ev.target as Element, ".x4timeline > .item" );
|
|
142
|
+
if( el ) {
|
|
143
|
+
this.fire( "click", { context: el.getInternalData( "item" ) } );
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
**/
|
|
16
16
|
|
|
17
17
|
import { Component, wrapDOM } from '../../core/component';
|
|
18
|
-
import {
|
|
18
|
+
import { ITipHandler, Point, Rect, Timer, UnsafeHtml, unsafeHtml } from '../../core/core_tools';
|
|
19
19
|
|
|
20
20
|
import { HBox } from '../boxes/boxes';
|
|
21
21
|
import { Popup, PopupProps } from '../popup/popup';
|
|
@@ -117,8 +117,8 @@ function closeTT( ) {
|
|
|
117
117
|
* ```
|
|
118
118
|
*/
|
|
119
119
|
|
|
120
|
-
@class_ns( "x4" )
|
|
121
120
|
class Tooltip extends Popup {
|
|
121
|
+
static classPrefix = "x4";
|
|
122
122
|
|
|
123
123
|
constructor( props: PopupProps ) {
|
|
124
124
|
super( props );
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
* that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
|
|
15
15
|
**/
|
|
16
16
|
|
|
17
|
-
import { asap
|
|
17
|
+
import { asap } from '../../core/core_tools';
|
|
18
18
|
import { EventCallback } from '../../core/core_events';
|
|
19
|
-
import { Component, ComponentEvent, ComponentEvents, ComponentProps, EvClick, EvDblClick, EvSelectionChange
|
|
19
|
+
import { Component, ComponentEvent, ComponentEvents, ComponentProps, EvClick, EvDblClick, EvSelectionChange } from '../../core/component';
|
|
20
20
|
|
|
21
21
|
import { ScrollView, Viewport } from '../viewport/viewport';
|
|
22
22
|
import { Label, SimpleText } from '../label/label';
|
|
@@ -84,8 +84,8 @@ export interface TreeviewEvents extends ComponentEvents {
|
|
|
84
84
|
* ```
|
|
85
85
|
*/
|
|
86
86
|
|
|
87
|
-
@class_ns( "x4" )
|
|
88
87
|
class CTreeViewItem extends Box {
|
|
88
|
+
static classPrefix = "x4";
|
|
89
89
|
|
|
90
90
|
private _item: TreeItem;
|
|
91
91
|
private _label: Component;
|
|
@@ -187,8 +187,8 @@ class CTreeViewItem extends Box {
|
|
|
187
187
|
*
|
|
188
188
|
*/
|
|
189
189
|
|
|
190
|
-
@class_ns( "x4" )
|
|
191
190
|
export class Treeview extends Component<TreeviewProps,TreeviewEvents> {
|
|
191
|
+
static classPrefix = "x4";
|
|
192
192
|
|
|
193
193
|
private _view: Viewport;
|
|
194
194
|
private _selection: ListboxID;
|
|
@@ -244,30 +244,24 @@ export class Treeview extends Component<TreeviewProps,TreeviewEvents> {
|
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
private _on_click( ev: UIEvent ) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
else {
|
|
260
|
-
this.fire('dblClick', fev );
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (!fev.defaultPrevented) {
|
|
264
|
-
this._selectItem( id, c );
|
|
265
|
-
}
|
|
247
|
+
// an item of this tree, not of a component around it
|
|
248
|
+
const c = Component.closest( ev.target as Element, ".item" );
|
|
249
|
+
if( c && this.contains( c ) ) {
|
|
250
|
+
const id = c.getInternalData( "id" );
|
|
251
|
+
|
|
252
|
+
const fev: ComponentEvent = { context:id };
|
|
253
|
+
if (ev.type == 'click') {
|
|
254
|
+
this.fire('click', fev );
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
this.fire('dblClick', fev );
|
|
258
|
+
}
|
|
266
259
|
|
|
267
|
-
|
|
260
|
+
if (!fev.defaultPrevented) {
|
|
261
|
+
this._selectItem( id, c );
|
|
268
262
|
}
|
|
269
263
|
|
|
270
|
-
|
|
264
|
+
return;
|
|
271
265
|
}
|
|
272
266
|
|
|
273
267
|
this.clearSelection( );
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
**/
|
|
16
16
|
|
|
17
17
|
import { Component, ComponentProps } from '../../core/component';
|
|
18
|
-
import { class_ns } from '../../core/core_tools';
|
|
19
18
|
import { Box } from '../boxes/boxes';
|
|
20
19
|
|
|
21
20
|
import "./video.module.scss"
|
|
@@ -31,8 +30,8 @@ interface VideoProps extends ComponentProps {
|
|
|
31
30
|
*
|
|
32
31
|
*/
|
|
33
32
|
|
|
34
|
-
@class_ns( "x4" )
|
|
35
33
|
export class Video extends Box {
|
|
34
|
+
static classPrefix = "x4";
|
|
36
35
|
|
|
37
36
|
private _vplayer: HTMLVideoElement;
|
|
38
37
|
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
* that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
|
|
15
15
|
**/
|
|
16
16
|
|
|
17
|
-
import { class_ns } from '../../core/core_tools';
|
|
18
17
|
import { Component, ComponentProps } from "../../core/component"
|
|
19
18
|
|
|
20
19
|
import "./viewport.module.scss"
|
|
@@ -24,8 +23,9 @@ import { Box, BoxProps } from '../components';
|
|
|
24
23
|
*
|
|
25
24
|
*/
|
|
26
25
|
|
|
27
|
-
@class_ns( "x4" )
|
|
28
26
|
export class Viewport extends Component {
|
|
27
|
+
static classPrefix = "x4";
|
|
28
|
+
|
|
29
29
|
constructor( props: ComponentProps ) {
|
|
30
30
|
super( props );
|
|
31
31
|
}
|
|
@@ -35,8 +35,9 @@ export class Viewport extends Component {
|
|
|
35
35
|
*
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
|
-
@class_ns( "x4" )
|
|
39
38
|
export class ScrollView extends Box {
|
|
39
|
+
static classPrefix = "x4";
|
|
40
|
+
|
|
40
41
|
constructor( props: BoxProps ) {
|
|
41
42
|
super( props );
|
|
42
43
|
this.setContent( new Viewport( {} ) );
|