x4js 2.3.29 → 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 +6 -3
- package/package.json +2 -1
- package/src/components/assets/icons.ts +2 -0
- package/src/components/dialog/dialog.module.scss +2 -0
- package/src/components/dialog/dialog.ts +1 -0
- package/src/components/menu/menu.module.scss +4 -6
- package/src/components/menu/menu.ts +4 -2
- package/src/components/popup/popup.ts +29 -3
- package/src/components/shared.scss +2 -1
- package/src/components/themes.scss +18 -10
- package/src/components/tooltips/tooltips.scss +8 -0
- package/src/components/tooltips/tooltips.ts +31 -0
- package/src/components/menu/caret-right-solid.svg +0 -1
package/README.md
CHANGED
package/aicontext.md
CHANGED
|
@@ -1335,7 +1335,8 @@ class Tabs extends Box {
|
|
|
1335
1335
|
|
|
1336
1336
|
```ts
|
|
1337
1337
|
interface PopupProps extends ComponentProps {
|
|
1338
|
-
autoClose?: boolean | string // true, or a group name
|
|
1338
|
+
autoClose?: boolean | string // true, or a group name: closed by a press outside; while it is shown,
|
|
1339
|
+
// a wheel outside every popup is ignored (the page does not scroll)
|
|
1339
1340
|
sizable?: boolean; movable?: boolean
|
|
1340
1341
|
}
|
|
1341
1342
|
class Popup extends Box {
|
|
@@ -1835,13 +1836,13 @@ by the browser, in its own locale, whatever this one.
|
|
|
1835
1836
|
/* the globals the components use: --background-primary / -secondary / -ternary / -hover / -active / -input,
|
|
1836
1837
|
--text-primary / -secondary / -ternary, --border / -light / -hover / -focus, --accent-background / -hover /
|
|
1837
1838
|
-focus / -active / -light, --accent-color (the text on the accent), --disabled-*, --icon-color / -active,
|
|
1838
|
-
--alert-*, --warning-*, --success
|
|
1839
|
+
--alert-*, --warning-*, --success-*, --shadow-color (the shadow of what floats: dialog, list, notification, tooltip) */
|
|
1839
1840
|
```
|
|
1840
1841
|
// The greys are mixes of a0 and a100: a theme that reverses the scale gets them light on dark.
|
|
1841
1842
|
// Each component reads its own variables (the @cssvar of its class), which point to these globals:
|
|
1842
1843
|
// a theme redefines the globals, or one variable of a component.
|
|
1843
1844
|
// Themes: Application.setTheme(name) puts the class x4theme-<name> on <html>. "" is the light theme (by default),
|
|
1844
|
-
// "dark" the dark theme of x4 (the greys of VS Code Dark Modern, its blues from #
|
|
1845
|
+
// "dark" the dark theme of x4 (the greys of VS Code Dark Modern, its blues from #2d6fa5: the accent #245883). An application adds its own theme in its css,
|
|
1845
1846
|
// html.x4theme-pink { --color-primary-a0: ...; ... }, then Application.setTheme("pink").
|
|
1846
1847
|
// The font: --font-name and --font-size (14px), on :root (normalize.scss); body takes them. To change the size
|
|
1847
1848
|
// of the text, set --font-size ( :root { --font-size: 13px } ), not the font-size of <html>: the components
|
|
@@ -2218,6 +2219,8 @@ class CMover {
|
|
|
2218
2219
|
}
|
|
2219
2220
|
createPainter(c2d: CanvasRenderingContext2D, w: number, h: number): CanvasEx // Canvas painter
|
|
2220
2221
|
initTooltips(): void // initialize the global tooltip system
|
|
2222
|
+
// a tooltip: 30vw wide at most (--tooltip-max-width; none: the width of its text); it goes at a click, and when its
|
|
2223
|
+
// element is removed or hidden (checked every second while it is shown)
|
|
2221
2224
|
```
|
|
2222
2225
|
|
|
2223
2226
|
JSX, in `.tsx` files: in tsconfig.json `"jsx": "react"` and `"jsxFactory": "x4_react.create_element"` (esbuild reads
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x4js",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.30",
|
|
4
|
+
"description": "A full TypeScript framework for web and desktop applications: components written as classes (layouts, inputs, lists, grids, dialogs, forms, charts...) and a command line to create, serve and build them.",
|
|
4
5
|
"license": "MIT",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"main": "src/x4.ts",
|
|
@@ -19,6 +19,8 @@ const x4icons = {
|
|
|
19
19
|
danger: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>`,
|
|
20
20
|
loading: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor"><!--!Font Awesome Pro 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2024 Fonticons, Inc.--><path d="M207.4 20.4c2.4 8.5-2.6 17.3-11.2 19.7C101.5 66.2 32 153 32 256c0 123.7 100.3 224 224 224s224-100.3 224-224c0-103-69.5-189.8-164.3-215.9c-8.5-2.4-13.5-11.2-11.2-19.7s11.2-13.5 19.7-11.2C432.5 39.1 512 138.2 512 256c0 141.4-114.6 256-256 256S0 397.4 0 256C0 138.2 79.5 39.1 187.7 9.2c8.5-2.4 17.3 2.6 19.7 11.2z"/></svg>`,
|
|
21
21
|
close_box: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" fill="currentColor"><!--!Font Awesome Pro 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2024 Fonticons, Inc.--><path d="M192 233.4L59.5 100.9 36.9 123.5 169.4 256 36.9 388.5l22.6 22.6L192 278.6 324.5 411.1l22.6-22.6L214.6 256 347.1 123.5l-22.6-22.6L192 233.4z"/></svg>`,
|
|
22
|
+
// a triangle to the right: a menu item that opens a sub menu
|
|
23
|
+
caret: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512" fill="currentColor"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M246.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-128-128c-9.2-9.2-22.9-11.9-34.9-6.9s-19.8 16.6-19.8 29.6l0 256c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l128-128z"/></svg>`,
|
|
22
24
|
// a triangle, a "!" cut in it
|
|
23
25
|
warning: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor"><path fill-rule="evenodd" d="M8 1L15.5 14.5H.5zM7.25 5.5h1.5V10h-1.5zM7 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0z"/></svg>`,
|
|
24
26
|
};
|
|
@@ -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
|
|
|
@@ -92,12 +92,10 @@
|
|
|
92
92
|
font-weight: 600;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
width: 6px;
|
|
100
|
-
}
|
|
95
|
+
// the triangle of a sub menu: after the text, which takes the width, at the right
|
|
96
|
+
#sub {
|
|
97
|
+
width: 6px;
|
|
98
|
+
height: 12px;
|
|
101
99
|
}
|
|
102
100
|
|
|
103
101
|
&:hover:not(.title),
|
|
@@ -95,8 +95,10 @@ class CMenuItem extends Component {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
this.setContent( [
|
|
98
|
-
new Icon({ id:"icon", iconId }),
|
|
99
|
-
new Component( { id: "text", content: itm.text } )
|
|
98
|
+
new Icon({ id:"icon", iconId }),
|
|
99
|
+
new Component( { id: "text", content: itm.text } ),
|
|
100
|
+
// a sub menu: a triangle at the right, in the colour of the text
|
|
101
|
+
itm.menu ? new Icon( { id: "sub", iconId: icons.caret } ) : null,
|
|
100
102
|
]);
|
|
101
103
|
|
|
102
104
|
if( itm.menu ) {
|
|
@@ -62,6 +62,32 @@ function dismissLast( e: UIEvent ) {
|
|
|
62
62
|
autoclose_list[autoclose_list.length-1].dismiss( );
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* a wheel outside every popup is ignored: the page under them does not scroll, it would leave
|
|
67
|
+
* them where they were. In a popup (a long menu), it scrolls as usual
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
function ignoreWheel( e: WheelEvent ) {
|
|
71
|
+
if( !autoclose_list.some( x => x.contains( e.target as Node ) ) ) {
|
|
72
|
+
e.preventDefault( );
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* the listeners of the document while a popup that closes by itself is shown
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
function watchOutside( on: boolean ) {
|
|
81
|
+
if( on ) {
|
|
82
|
+
document.addEventListener( "pointerdown", dismissLast, true );
|
|
83
|
+
document.addEventListener( "wheel", ignoreWheel, { capture: true, passive: false } );
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
document.removeEventListener( "pointerdown", dismissLast, true );
|
|
87
|
+
document.removeEventListener( "wheel", ignoreWheel, true );
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
65
91
|
/**
|
|
66
92
|
* @cssvar
|
|
67
93
|
* ```
|
|
@@ -256,7 +282,7 @@ export class Popup<P extends PopupProps = PopupProps, E extends PopupEvents = Po
|
|
|
256
282
|
if( idx>=0 ) {
|
|
257
283
|
autoclose_list.splice( idx, 1 );
|
|
258
284
|
if( autoclose_list.length==0 ) {
|
|
259
|
-
|
|
285
|
+
watchOutside( false );
|
|
260
286
|
}
|
|
261
287
|
}
|
|
262
288
|
}
|
|
@@ -295,7 +321,7 @@ export class Popup<P extends PopupProps = PopupProps, E extends PopupEvents = Po
|
|
|
295
321
|
|
|
296
322
|
if( this.props.autoClose ) {
|
|
297
323
|
if( autoclose_list.length==0 ) {
|
|
298
|
-
|
|
324
|
+
watchOutside( true );
|
|
299
325
|
}
|
|
300
326
|
|
|
301
327
|
autoclose_list.push( this );
|
|
@@ -384,7 +410,7 @@ export class Popup<P extends PopupProps = PopupProps, E extends PopupEvents = Po
|
|
|
384
410
|
const list = inc_group.reverse( );
|
|
385
411
|
autoclose_list = excl_group;
|
|
386
412
|
if( autoclose_list.length==0 ) {
|
|
387
|
-
|
|
413
|
+
watchOutside( false );
|
|
388
414
|
}
|
|
389
415
|
|
|
390
416
|
list.forEach( x => x.close() );
|
|
@@ -62,8 +62,9 @@
|
|
|
62
62
|
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// its colour from the theme (--shadow-color): darker on a dark background
|
|
65
66
|
%shadow-xl {
|
|
66
|
-
box-shadow: 0 20px 25px -5px
|
|
67
|
+
box-shadow: 0 20px 25px -5px var( --shadow-color ), 0 8px 10px -6px var( --shadow-color );
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
%shadow-xxl {
|
|
@@ -19,8 +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
|
-
|
|
22
|
+
// the blues of the dark theme, from their own base, with the intervals of the light one: its accent
|
|
23
|
+
// (a60, the base less one interval) #245883
|
|
24
|
+
$color--dark-base: #2d6fa5;
|
|
24
25
|
|
|
25
26
|
:root {
|
|
26
27
|
--color-primary-a0: #ffffff;
|
|
@@ -93,6 +94,9 @@ $color--dark-base: #5796ca;
|
|
|
93
94
|
--border-hover: color-mix(in srgb, var(--color-primary-a100) 23.9%, var(--color-primary-a0)); // #c2c2c2
|
|
94
95
|
--border-focus: var(--accent-background);
|
|
95
96
|
|
|
97
|
+
// the shadow of what floats over the page (%shadow-xl: a dialog, a list, a notification, a tooltip)
|
|
98
|
+
--shadow-color: rgb(0 0 0 / 0.1);
|
|
99
|
+
|
|
96
100
|
// the red, orange and green do not follow the scale: their text stays white
|
|
97
101
|
--alert-background: var(--color-danger-a50);
|
|
98
102
|
--alert-background-active: var(--color-danger-a60);
|
|
@@ -123,14 +127,14 @@ html.x4theme-dark {
|
|
|
123
127
|
--color-primary-a20: #37373d; // list.inactiveSelectionBackground
|
|
124
128
|
--color-primary-a30: #3c3c3c; // input.border
|
|
125
129
|
|
|
126
|
-
// the blues: from $color--dark-base (chosen)
|
|
127
|
-
//
|
|
128
|
-
--color-primary-a40: #{color.adjust($color--dark-base, $lightness: -
|
|
129
|
-
--color-primary-a50: #{$color--dark-base};
|
|
130
|
-
--color-primary-a55: #{color.adjust($color--dark-base, $lightness: -5%)};
|
|
131
|
-
--color-primary-a60: #{$color--dark-base};
|
|
132
|
-
--color-primary-a70: #{color.adjust($color--dark-base, $lightness:
|
|
133
|
-
--color-primary-a80: #{color.adjust($color--dark-base, $lightness:
|
|
130
|
+
// the blues: from $color--dark-base (chosen), as the light theme makes them; a40, a selection,
|
|
131
|
+
// where VS Code has list.activeSelectionBackground
|
|
132
|
+
--color-primary-a40: #{color.adjust($color--dark-base, $lightness: -19.4%)}; // a selection
|
|
133
|
+
--color-primary-a50: #{$color--dark-base};
|
|
134
|
+
--color-primary-a55: #{color.adjust($color--dark-base, $lightness: -5%)}; // the accent hovered
|
|
135
|
+
--color-primary-a60: #{color.adjust($color--dark-base, $lightness: -$color--interval)}; // the accent
|
|
136
|
+
--color-primary-a70: #{color.adjust($color--dark-base, $lightness: -$color--interval * 2)}; // focused
|
|
137
|
+
--color-primary-a80: #{color.adjust($color--dark-base, $lightness: -$color--interval * 3)}; // active
|
|
134
138
|
|
|
135
139
|
--color-primary-a90: #cccccc;
|
|
136
140
|
--color-primary-a100: #cccccc; // foreground
|
|
@@ -162,6 +166,10 @@ html.x4theme-dark {
|
|
|
162
166
|
--color-dialog-caption: #181818; // titleBar.activeBackground
|
|
163
167
|
--color-dialog-caption-text: #cccccc; // titleBar.activeForeground
|
|
164
168
|
--color-dialog-background: #202020; // editorWidget.background
|
|
169
|
+
--color-dialog-border: #313131; // widget.border: its edge on the dark background
|
|
170
|
+
|
|
171
|
+
// a shadow seen on a dark background: darker
|
|
172
|
+
--shadow-color: rgb(0 0 0 / 0.5);
|
|
165
173
|
|
|
166
174
|
// the tiles of a MapView are light: turned dark, their colours kept
|
|
167
175
|
--mapview-tile-filter: invert( 1 ) hue-rotate( 180deg ) brightness( 0.95 ) contrast( 0.9 );
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
--tooltip-color: var( --color-primary-a90 );
|
|
28
28
|
--tooltip-border: var( --color-primary-a20 );
|
|
29
29
|
--tooltip-image-background: var( --background-primary );
|
|
30
|
+
|
|
31
|
+
--tooltip-max-width: 30vw; // a long text goes on the next line; none: the width of the text
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
.x4tooltip {
|
|
@@ -41,6 +43,12 @@
|
|
|
41
43
|
border: 1px solid var( --tooltip-border );
|
|
42
44
|
background-color: var( --tooltip-background );
|
|
43
45
|
width: max-content;
|
|
46
|
+
max-width: var( --tooltip-max-width );
|
|
47
|
+
|
|
48
|
+
// its content (a Box: it does not shrink) as wide as the tooltip at most: the text goes on the next line
|
|
49
|
+
> .x4hbox {
|
|
50
|
+
flex-shrink: 1;
|
|
51
|
+
}
|
|
44
52
|
|
|
45
53
|
.x4icon {
|
|
46
54
|
padding: 2px 4px;
|
|
@@ -72,6 +72,14 @@ export function initTooltips( ) {
|
|
|
72
72
|
|
|
73
73
|
}, true );
|
|
74
74
|
|
|
75
|
+
// a click: no tooltip, it would stay over what the click opens
|
|
76
|
+
document.addEventListener( "mousedown", ( ) => {
|
|
77
|
+
if( last_hit ) {
|
|
78
|
+
last_hit = null;
|
|
79
|
+
closeTT( );
|
|
80
|
+
}
|
|
81
|
+
}, true );
|
|
82
|
+
|
|
75
83
|
document.addEventListener( "mousemove", ( ev: MouseEvent ) => {
|
|
76
84
|
if( last_hit ) {
|
|
77
85
|
mouse_pos = { x:ev.pageX, y:ev.pageY }
|
|
@@ -85,6 +93,11 @@ function showTT( text: string, rc: Rect, pt: Point ) {
|
|
|
85
93
|
}
|
|
86
94
|
|
|
87
95
|
timer.setTimeout( "update", 300, ( ) => {
|
|
96
|
+
// its element gone in the meantime
|
|
97
|
+
if( !last_hit || isGone( last_hit ) ) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
88
101
|
tooltip.setText( unsafeHtml(text) );
|
|
89
102
|
|
|
90
103
|
let y = mouse_pos.y;
|
|
@@ -94,12 +107,29 @@ function showTT( text: string, rc: Rect, pt: Point ) {
|
|
|
94
107
|
|
|
95
108
|
//tooltip.displayNear( rc, "top left", "bottom left", {x:0,y:4} );
|
|
96
109
|
tooltip.displayAt( mouse_pos.x+17, y+5 );
|
|
110
|
+
|
|
111
|
+
// an element removed or hidden under the mouse has no mouseleave: it is checked every second
|
|
112
|
+
timer.setInterval( "check", 1000, ( ) => {
|
|
113
|
+
if( !last_hit || isGone( last_hit ) ) {
|
|
114
|
+
last_hit = null;
|
|
115
|
+
closeTT( );
|
|
116
|
+
}
|
|
117
|
+
} );
|
|
97
118
|
} );
|
|
98
119
|
}
|
|
99
120
|
|
|
100
121
|
function closeTT( ) {
|
|
101
122
|
tooltip.show( false );
|
|
102
123
|
timer.clearTimeout( "update" );
|
|
124
|
+
timer.clearInterval( "check" );
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* the element of the tooltip, not shown any more: removed from the page, or hidden
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
function isGone( el: HTMLElement ) {
|
|
132
|
+
return !el.isConnected || el.getClientRects( ).length==0;
|
|
103
133
|
}
|
|
104
134
|
|
|
105
135
|
/**
|
|
@@ -114,6 +144,7 @@ function closeTT( ) {
|
|
|
114
144
|
* --tooltip-color
|
|
115
145
|
* --tooltip-border
|
|
116
146
|
* --tooltip-image-background
|
|
147
|
+
* --tooltip-max-width
|
|
117
148
|
* ```
|
|
118
149
|
*/
|
|
119
150
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512" fill="var(--color-gray-8)"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M246.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-128-128c-9.2-9.2-22.9-11.9-34.9-6.9s-19.8 16.6-19.8 29.6l0 256c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l128-128z"/></svg>
|