x4js 2.3.28 → 2.3.30
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/README.md +1 -1
- package/aicontext.md +385 -21
- package/package.json +8 -3
- package/src/components/assets/icons.ts +2 -0
- 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/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/combobox/combobox.module.scss +2 -1
- package/src/components/components.ts +3 -0
- package/src/components/dialog/dialog.module.scss +2 -0
- package/src/components/dialog/dialog.ts +1 -0
- package/src/components/gantt/gantt.module.scss +462 -0
- package/src/components/gantt/gantt.ts +1254 -0
- 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/menu/menu.module.scss +4 -6
- package/src/components/menu/menu.ts +4 -2
- package/src/components/planner/planner.module.scss +688 -0
- package/src/components/planner/planner.ts +1792 -0
- package/src/components/popup/popup.ts +29 -3
- package/src/components/radio/radio.module.scss +4 -1
- package/src/components/shared.scss +2 -1
- package/src/components/slider/slider.module.scss +22 -20
- package/src/components/switch/switch.module.scss +68 -44
- package/src/components/switch/switch.ts +6 -3
- package/src/components/tag/tag.module.scss +4 -4
- package/src/components/textedit/textedit.module.scss +5 -2
- package/src/components/themes.scss +27 -6
- package/src/components/tooltips/tooltips.scss +8 -0
- package/src/components/tooltips/tooltips.ts +31 -0
- package/src/core/core_i18n.ts +65 -0
- package/src/core/core_tools.ts +44 -0
- package/src/x4doc.ts +3 -0
- package/src/components/menu/caret-right-solid.svg +0 -1
|
@@ -21,7 +21,7 @@ import { clamp, formatNumber, IRect, isNumber, sanitizeHtml } from '../../core/c
|
|
|
21
21
|
|
|
22
22
|
import "./chart.module.scss"
|
|
23
23
|
|
|
24
|
-
export type ChartType = "line" | "area" | "bar" | "pie" | "donut";
|
|
24
|
+
export type ChartType = "line" | "area" | "bar" | "pie" | "donut" | "radar";
|
|
25
25
|
|
|
26
26
|
export interface ChartTick {
|
|
27
27
|
value: number; // on x axis: index of the point
|
|
@@ -38,9 +38,9 @@ export interface ChartAxis {
|
|
|
38
38
|
|
|
39
39
|
export interface ChartSerie {
|
|
40
40
|
name?: string;
|
|
41
|
-
values: number[]; // undefined: no value (hole in the line)
|
|
41
|
+
values: number[]; // undefined: no value (hole in the line; at the center of a radar)
|
|
42
42
|
color?: string; // default: palette --chart-color-1..8
|
|
43
|
-
fill?: string; // area fill color
|
|
43
|
+
fill?: string; // area fill color ("none" on a radar: the line only)
|
|
44
44
|
dots?: boolean; // always show the points
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -57,7 +57,7 @@ interface ChartEvents extends ComponentEvents {
|
|
|
57
57
|
export interface ChartProps extends Omit<SvgProps,"svg"|"viewbox"|"content"> {
|
|
58
58
|
type?: ChartType; // default "line"
|
|
59
59
|
series: ChartSerie[]; // pie & donut use the first serie
|
|
60
|
-
labels?: string[]; // default: 1, 2, 3
|
|
60
|
+
labels?: string[]; // default: 1, 2, 3...; radar: the name of each axis
|
|
61
61
|
xaxis?: ChartAxis;
|
|
62
62
|
yaxis?: ChartAxis;
|
|
63
63
|
legend?: boolean; // default: shown when there is more than one named serie
|
|
@@ -211,7 +211,10 @@ function drawBar( grp: SvgGroup, x: number, w: number, y0: number, y1: number, r
|
|
|
211
211
|
|
|
212
212
|
|
|
213
213
|
/**
|
|
214
|
-
* Minimal chart (line, area, bar, pie, donut) drawn in svg.
|
|
214
|
+
* Minimal chart (line, area, bar, pie, donut, radar) drawn in svg.
|
|
215
|
+
*
|
|
216
|
+
* radar: an axis for each point, from the center (yaxis.min, 0 by default) to the outer ring
|
|
217
|
+
* (yaxis.max); each serie is a closed line, tinted inside. At least 3 points.
|
|
215
218
|
*
|
|
216
219
|
* @example
|
|
217
220
|
* new Chart( {
|
|
@@ -335,6 +338,11 @@ export class Chart extends SvgComponent<ChartProps,ChartEvents> {
|
|
|
335
338
|
* make an element clickable with a tooltip
|
|
336
339
|
*/
|
|
337
340
|
|
|
341
|
+
private _tip( serie: number, index: number, value: number ) {
|
|
342
|
+
const name = this._series[serie].name ? ` · ${this._series[serie].name}` : '';
|
|
343
|
+
return `${this._label(index)}${name}: ${this._format(value)}`;
|
|
344
|
+
}
|
|
345
|
+
|
|
338
346
|
private _mark<T extends SvgShape | SvgPath>( item: T, serie: number, index: number, tip: string ): T {
|
|
339
347
|
item.setAttr( "data-serie", serie+"" )
|
|
340
348
|
.setAttr( "data-index", index+"" )
|
|
@@ -381,6 +389,9 @@ export class Chart extends SvgComponent<ChartProps,ChartEvents> {
|
|
|
381
389
|
if( pie ) {
|
|
382
390
|
this._drawPie( bld, area );
|
|
383
391
|
}
|
|
392
|
+
else if( this._type=="radar" ) {
|
|
393
|
+
this._drawRadar( bld, area );
|
|
394
|
+
}
|
|
384
395
|
else {
|
|
385
396
|
this._drawXY( bld, area );
|
|
386
397
|
}
|
|
@@ -648,11 +659,6 @@ export class Chart extends SvgComponent<ChartProps,ChartEvents> {
|
|
|
648
659
|
grp.clip( id );
|
|
649
660
|
}
|
|
650
661
|
|
|
651
|
-
const tip = ( s: number, i: number, v: number ) => {
|
|
652
|
-
const name = series[s].name ? ` · ${series[s].name}` : '';
|
|
653
|
-
return `${this._label(i)}${name}: ${this._format(v)}`;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
662
|
if( band ) {
|
|
657
663
|
const n = stacked ? 1 : Math.max( 1, series.length );
|
|
658
664
|
const w = Math.min( xstep*0.8/n, MAX_BAR );
|
|
@@ -684,7 +690,7 @@ export class Chart extends SvgComponent<ChartProps,ChartEvents> {
|
|
|
684
690
|
}
|
|
685
691
|
|
|
686
692
|
this._paint( bar.addClass( "bar" ), s, serie.color );
|
|
687
|
-
this._mark( bar, s, i,
|
|
693
|
+
this._mark( bar, s, i, this._tip( s, i, v ) );
|
|
688
694
|
}
|
|
689
695
|
});
|
|
690
696
|
}
|
|
@@ -757,11 +763,163 @@ export class Chart extends SvgComponent<ChartProps,ChartEvents> {
|
|
|
757
763
|
for( const p of pts ) {
|
|
758
764
|
const dot = dots.circle( p.x, p.y, 4 ).addClass( visible ? "dot" : "dot ghost" );
|
|
759
765
|
this._paint( dot, s, serie.color );
|
|
760
|
-
this._mark( dot, s, p.i,
|
|
766
|
+
this._mark( dot, s, p.i, this._tip( s, p.i, value( s, p.i ) ?? 0 ) );
|
|
761
767
|
}
|
|
762
768
|
}
|
|
763
769
|
}
|
|
764
770
|
});
|
|
765
771
|
}
|
|
766
772
|
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* radar: an axis for each point, a ring for each tick of the y scale
|
|
776
|
+
*/
|
|
777
|
+
|
|
778
|
+
private _drawRadar( bld: SvgBuilder, area: IRect ) {
|
|
779
|
+
|
|
780
|
+
const series = this._series;
|
|
781
|
+
const xaxis = this._xaxis;
|
|
782
|
+
const yaxis = this._yaxis;
|
|
783
|
+
|
|
784
|
+
// number of axes
|
|
785
|
+
let count = xaxis.max ?? 0;
|
|
786
|
+
if( xaxis.max===undefined ) {
|
|
787
|
+
series.forEach( s => { count = Math.max( count, s.values?.length ?? 0 ); } );
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// less than 3 axes make no surface
|
|
791
|
+
if( count<3 ) {
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
const value = ( s: number, i: number ) => {
|
|
796
|
+
const v = series[s].values?.[i];
|
|
797
|
+
return isNumber(v) ? v : undefined;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// room for the labels around
|
|
801
|
+
let maxw = 0;
|
|
802
|
+
for( let i=0; i<count; i++ ) {
|
|
803
|
+
maxw = Math.max( maxw, textWidth( this._label(i) ) );
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const cx = area.left + area.width/2;
|
|
807
|
+
const cy = area.top + area.height/2;
|
|
808
|
+
const radius = Math.min( area.width/2 - maxw - 8, area.height/2 - 16 );
|
|
809
|
+
|
|
810
|
+
if( radius<=10 ) {
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// scale: the center at 0, lower for negative values
|
|
815
|
+
let dmin = 0;
|
|
816
|
+
let dmax = -Infinity;
|
|
817
|
+
|
|
818
|
+
series.forEach( ( _, s ) => {
|
|
819
|
+
for( let i=0; i<count; i++ ) {
|
|
820
|
+
const v = value( s, i );
|
|
821
|
+
if( v!==undefined ) {
|
|
822
|
+
dmin = Math.min( dmin, v );
|
|
823
|
+
dmax = Math.max( dmax, v );
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
if( dmax==-Infinity ) {
|
|
829
|
+
dmax = 1;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
const nice = niceScale( yaxis.min ?? dmin, yaxis.max ?? dmax, clamp( Math.floor( radius/30 ), 2, 6 ) );
|
|
833
|
+
const min = yaxis.min ?? nice.min;
|
|
834
|
+
let max = yaxis.max ?? nice.max;
|
|
835
|
+
if( max<=min ) {
|
|
836
|
+
max = min+1;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
const step = yaxis.step>0 ? yaxis.step : nice.step;
|
|
840
|
+
const rings = ( yaxis.ticks ?? makeTicks( min, max, step, v => this._format(v) ) ).filter( t => t.value>min && t.value<=max );
|
|
841
|
+
|
|
842
|
+
// the axis i: 0 at the top, clockwise; a value out of the bounds stays on them
|
|
843
|
+
const angle = ( i: number ) => ( i/count*360 - 90 ) * Math.PI/180;
|
|
844
|
+
const point = ( i: number, v: number ) => {
|
|
845
|
+
const r = ( clamp( v, min, max ) - min ) / ( max-min ) * radius;
|
|
846
|
+
return { x: cx + r*Math.cos( angle(i) ), y: cy + r*Math.sin( angle(i) ) };
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
const polygon = ( grp: SvgGroup, v: ( i: number ) => number ) => {
|
|
850
|
+
const pth = grp.path( );
|
|
851
|
+
for( let i=0; i<count; i++ ) {
|
|
852
|
+
const p = point( i, v(i) );
|
|
853
|
+
if( i==0 ) {
|
|
854
|
+
pth.moveTo( p.x, p.y );
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
pth.lineTo( p.x, p.y );
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
return pth.closePath( );
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// grid & axes
|
|
865
|
+
const grid = bld.group( ).addClass( "axis radar" );
|
|
866
|
+
|
|
867
|
+
for( const t of rings ) {
|
|
868
|
+
polygon( grid, ( ) => t.value ).addClass( "grid" );
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
for( let i=0; i<count; i++ ) {
|
|
872
|
+
const p = point( i, max );
|
|
873
|
+
grid.path( ).moveTo( cx, cy ).lineTo( p.x, p.y ).addClass( "base" );
|
|
874
|
+
|
|
875
|
+
// the label beyond the end of its axis, on the side of the axis
|
|
876
|
+
const cos = Math.cos( angle(i) );
|
|
877
|
+
const sin = Math.sin( angle(i) );
|
|
878
|
+
const halign = Math.abs( cos )<0.3 ? 'center' : ( cos>0 ? 'left' : 'right' );
|
|
879
|
+
const valign = Math.abs( sin )<0.3 ? 'center' : ( sin>0 ? 'top' : 'bottom' );
|
|
880
|
+
drawText( grid, cx + ( radius+8 )*cos, cy + ( radius+8 )*sin, this._label(i), halign, valign );
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// the values of the rings, along the first axis
|
|
884
|
+
for( const t of rings ) {
|
|
885
|
+
drawText( grid, cx+4, point( 0, t.value ).y, t.text, 'left', 'center' );
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// series
|
|
889
|
+
const grp = bld.group( ).addClass( "plot" );
|
|
890
|
+
const areas = grp.group( );
|
|
891
|
+
const lines = grp.group( );
|
|
892
|
+
const dots = grp.group( );
|
|
893
|
+
|
|
894
|
+
const ghosts = series.length*count<=MAX_GHOSTS;
|
|
895
|
+
|
|
896
|
+
series.forEach( ( serie, s ) => {
|
|
897
|
+
// a value missing is at the center
|
|
898
|
+
const at = ( i: number ) => value( s, i ) ?? min;
|
|
899
|
+
|
|
900
|
+
const pth = polygon( areas, at ).addClass( "area" );
|
|
901
|
+
this._paint( pth, s, serie.color );
|
|
902
|
+
|
|
903
|
+
if( serie.fill ) {
|
|
904
|
+
pth.setStyle( "fill", serie.fill ).setStyle( "fillOpacity", "1" );
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
this._paint( polygon( lines, at ).addClass( "line" ), s, serie.color );
|
|
908
|
+
|
|
909
|
+
// points: always visible, or only on hover (ghost)
|
|
910
|
+
if( serie.dots || ghosts ) {
|
|
911
|
+
for( let i=0; i<count; i++ ) {
|
|
912
|
+
const v = value( s, i );
|
|
913
|
+
if( v===undefined ) {
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const p = point( i, v );
|
|
918
|
+
const dot = dots.circle( p.x, p.y, 4 ).addClass( serie.dots ? "dot" : "dot ghost" );
|
|
919
|
+
this._paint( dot, s, serie.color );
|
|
920
|
+
this._mark( dot, s, i, this._tip( s, i, v ) );
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
}
|
|
767
925
|
}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
--combobox-border: var( --border );
|
|
24
24
|
--combobox-border-focus: var( --border-focus );
|
|
25
25
|
|
|
26
|
-
--combobox-btn-background: inherit
|
|
26
|
+
--combobox-btn-background: var( --background-input ); // the one of its field: inherit was the page (dark theme)
|
|
27
27
|
--combobox-btn-color: var( --text-secondary );
|
|
28
28
|
--combobox-btn-color-hover: var( --text-primary );
|
|
29
29
|
|
|
@@ -110,6 +110,7 @@
|
|
|
110
110
|
margin: 0;
|
|
111
111
|
padding: 0;
|
|
112
112
|
outline: none;
|
|
113
|
+
align-self: stretch; // the height of the field: no strip of the page above and under it
|
|
113
114
|
background-color: var( --combobox-btn-background );
|
|
114
115
|
color: var( --combobox-btn-color );
|
|
115
116
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from "./autocomplete/autocomplete"
|
|
2
|
+
export * from "./bargraph/bargraph"
|
|
2
3
|
export * from "./boxes/boxes"
|
|
3
4
|
export * from "./breadcrumb/breadcrumb"
|
|
4
5
|
export * from "./btngroup/btngroup"
|
|
@@ -6,6 +7,7 @@ export * from "./button/button"
|
|
|
6
7
|
export * from "./calendar/calendar"
|
|
7
8
|
export * from "./canvas/canvas"
|
|
8
9
|
export * from "./canvas/canvas_ex"
|
|
10
|
+
export * from "./carousel/carousel"
|
|
9
11
|
export * from "./chart/chart"
|
|
10
12
|
export * from "./checkbox/checkbox"
|
|
11
13
|
export * from "./colorinput/colorinput"
|
|
@@ -22,6 +24,7 @@ export * from "./icon/icon"
|
|
|
22
24
|
export * from "./image/image"
|
|
23
25
|
export * from "./input/input"
|
|
24
26
|
export * from "./keyboard/keyboard"
|
|
27
|
+
export * from "./knob/knob"
|
|
25
28
|
export * from "./label/label"
|
|
26
29
|
export * from "./link/link"
|
|
27
30
|
export * from "./listbox/listbox"
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
--color-dialog-caption: var( --accent-background-active );
|
|
21
21
|
--color-dialog-caption-text: var( --accent-color );
|
|
22
22
|
--color-dialog-background: var( --background-primary );
|
|
23
|
+
--color-dialog-border: transparent; // its shadow is enough on a light background
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
.x4dialog {
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
|
|
29
30
|
position: absolute !important;
|
|
30
31
|
background-color: var( --color-dialog-background );
|
|
32
|
+
border: 1px solid var( --color-dialog-border );
|
|
31
33
|
max-width: 99vw;
|
|
32
34
|
outline: none;
|
|
33
35
|
|