x4js 2.3.30 → 2.3.32
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 +40 -5
- package/aiguide.md +2 -0
- package/changelog.md +1357 -0
- package/package.json +2 -1
- package/src/components/button/button.module.scss +1 -1
- package/src/components/checkbox/checkbox.module.scss +4 -3
- package/src/components/checkbox/checkbox.ts +1 -0
- package/src/components/menu/menu.module.scss +59 -0
- package/src/components/menu/menu.ts +142 -0
- package/src/components/messages/circle-exclamation.svg +1 -1
- package/src/components/messages/circle-question.svg +1 -1
- package/src/components/messages/messages.module.scss +28 -0
- package/src/components/messages/messages.ts +7 -1
- package/src/components/notification/notification.module.scss +4 -0
- package/src/components/popup/popup.ts +22 -3
- package/src/components/radio/radio.module.scss +4 -5
- package/src/components/radio/radio.ts +1 -0
- package/src/components/spreadsheet/spreadsheet.ts +28 -21
- package/src/components/textedit/textedit.module.scss +1 -0
- package/src/components/textedit/textedit.ts +4 -1
- package/src/components/themes.scss +14 -0
- package/src/components/treeview/treeview.ts +44 -6
- package/src/core/core_clipboard.ts +25 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x4js",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.32",
|
|
4
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.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"aicli.md",
|
|
31
31
|
"aicontext.md",
|
|
32
32
|
"aiguide.md",
|
|
33
|
+
"changelog.md",
|
|
33
34
|
"README.md",
|
|
34
35
|
"LICENSE"
|
|
35
36
|
],
|
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
|
|
30
30
|
--checkbox-button-border: var( --border );
|
|
31
31
|
--checkbox-button-color: var( --text-ternary );
|
|
32
|
-
--checkbox-button-color-checked: var( --accent-
|
|
32
|
+
--checkbox-button-color-checked: var( --accent-text );
|
|
33
33
|
--checkbox-button-background-checked: var( --background-secondary );
|
|
34
34
|
--checkbox-button-border-checked: var( --accent-background );
|
|
35
|
+
--checkbox-button-icon-size: 24px; // set by a class of the application on a checkbox: it wins over :root
|
|
35
36
|
|
|
36
37
|
--checkbox-danger-color: var( --alert-background );
|
|
37
38
|
--checkbox-danger-background: var( --alert-background-light );
|
|
@@ -187,8 +188,8 @@
|
|
|
187
188
|
#icon {
|
|
188
189
|
color: var( --checkbox-ink );
|
|
189
190
|
fill: var( --checkbox-ink );
|
|
190
|
-
width:
|
|
191
|
-
height:
|
|
191
|
+
width: var( --checkbox-button-icon-size );
|
|
192
|
+
height: var( --checkbox-button-icon-size );
|
|
192
193
|
}
|
|
193
194
|
}
|
|
194
195
|
|
|
@@ -54,6 +54,7 @@ interface CheckboxProps extends ComponentProps {
|
|
|
54
54
|
* --checkbox-button-color-checked
|
|
55
55
|
* --checkbox-button-background-checked
|
|
56
56
|
* --checkbox-button-border-checked
|
|
57
|
+
* --checkbox-button-icon-size
|
|
57
58
|
* --checkbox-danger-color
|
|
58
59
|
* --checkbox-danger-background
|
|
59
60
|
* ```
|
|
@@ -25,6 +25,15 @@
|
|
|
25
25
|
--menuitem-color-active: var( --accent-color-active );
|
|
26
26
|
--menuitem-background-hover: var( --accent-background-hover );
|
|
27
27
|
--menuitem-color-hover: var( --accent-color-hover );
|
|
28
|
+
--menuitem-shortcut-color: var( --text-secondary );
|
|
29
|
+
|
|
30
|
+
--menubar-background: var( --background-primary );
|
|
31
|
+
--menubar-border: var( --border );
|
|
32
|
+
|
|
33
|
+
--menubaritem-color: var( --text-primary );
|
|
34
|
+
--menubaritem-background-hover: var( --accent-background-hover );
|
|
35
|
+
--menubaritem-color-hover: var( --accent-color-hover );
|
|
36
|
+
--menubaritem-color-disabled: var( --disabled-color-dark );
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
.x4menu {
|
|
@@ -84,6 +93,20 @@
|
|
|
84
93
|
@extend %flex;
|
|
85
94
|
}
|
|
86
95
|
|
|
96
|
+
// the keys of the command: after the text, which takes the width, at the right
|
|
97
|
+
#shortcut {
|
|
98
|
+
color: var(--menuitem-shortcut-color);
|
|
99
|
+
padding-left: 16px;
|
|
100
|
+
white-space: nowrap;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// hovered, active, disabled: in the colour of the text
|
|
104
|
+
&:hover, &.opened, &:active, &[disabled] {
|
|
105
|
+
#shortcut {
|
|
106
|
+
color: inherit;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
87
110
|
&.title {
|
|
88
111
|
color: var( --text-primary );
|
|
89
112
|
cursor: auto;
|
|
@@ -124,4 +147,40 @@
|
|
|
124
147
|
border-bottom: 1px solid var(--border);
|
|
125
148
|
margin-bottom: 4px;
|
|
126
149
|
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.x4menubar {
|
|
153
|
+
padding: 0 4px;
|
|
154
|
+
|
|
155
|
+
background-color: var(--menubar-background);
|
|
156
|
+
border-bottom: 1px solid var(--menubar-border);
|
|
157
|
+
|
|
158
|
+
.x4cmenubaritem {
|
|
159
|
+
@extend %hbox;
|
|
160
|
+
|
|
161
|
+
gap: 6px;
|
|
162
|
+
padding: 4px 10px;
|
|
163
|
+
color: var(--menubaritem-color);
|
|
164
|
+
cursor: pointer;
|
|
165
|
+
user-select: none;
|
|
166
|
+
|
|
167
|
+
transition: background-color 0.1s;
|
|
168
|
+
|
|
169
|
+
#icon {
|
|
170
|
+
width: 16px;
|
|
171
|
+
height: 16px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// hovered, or its menu open: as an item of the menu
|
|
175
|
+
&:hover:not([disabled]),
|
|
176
|
+
&.opened {
|
|
177
|
+
background-color: var(--menubaritem-background-hover);
|
|
178
|
+
color: var(--menubaritem-color-hover);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
&[disabled] {
|
|
182
|
+
color: var(--menubaritem-color-disabled);
|
|
183
|
+
cursor: auto;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
127
186
|
}
|
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
import { Component } from "../../core/component"
|
|
18
18
|
import { DOMEventHandler } from '../../core/core_dom';
|
|
19
19
|
import { Timer, UnsafeHtml, isFunction, isString } from '../../core/core_tools';
|
|
20
|
+
import { shortcutText } from '../../core/core_shortcuts';
|
|
20
21
|
|
|
21
22
|
import { Popup, PopupProps } from '../popup/popup';
|
|
22
23
|
import { Icon } from '../icon/icon';
|
|
24
|
+
import { BoxProps, HBox } from '../boxes/boxes';
|
|
23
25
|
|
|
24
26
|
import icons from "../assets/icons";
|
|
25
27
|
import "./menu.module.scss"
|
|
@@ -38,6 +40,13 @@ export interface MenuItem {
|
|
|
38
40
|
disabled?: boolean;
|
|
39
41
|
checked?: boolean | ( () => boolean);
|
|
40
42
|
click?: DOMEventHandler;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* the keys of the command, shown at the right of the item: "Mod+S" is written
|
|
46
|
+
* "Ctrl+S", "⌘S" on a Mac (shortcutText). Shown only: the shortcut itself
|
|
47
|
+
* is added by Application.addShortcut or Component.addShortcut
|
|
48
|
+
*/
|
|
49
|
+
shortcut?: string;
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
export type MenuElement = MenuItem | Component | string;
|
|
@@ -56,6 +65,7 @@ export interface MenuProps extends Omit<PopupProps,"content"> {
|
|
|
56
65
|
* --menuitem-color-active
|
|
57
66
|
* --menuitem-background-hover
|
|
58
67
|
* --menuitem-color-hover
|
|
68
|
+
* --menuitem-shortcut-color
|
|
59
69
|
* ```
|
|
60
70
|
*/
|
|
61
71
|
|
|
@@ -97,6 +107,7 @@ class CMenuItem extends Component {
|
|
|
97
107
|
this.setContent( [
|
|
98
108
|
new Icon({ id:"icon", iconId }),
|
|
99
109
|
new Component( { id: "text", content: itm.text } ),
|
|
110
|
+
itm.shortcut ? new Component( { id: "shortcut", content: shortcutText( itm.shortcut ) } ) : null,
|
|
100
111
|
// a sub menu: a triangle at the right, in the colour of the text
|
|
101
112
|
itm.menu ? new Icon( { id: "sub", iconId: icons.caret } ) : null,
|
|
102
113
|
]);
|
|
@@ -207,3 +218,134 @@ export class Menu extends Popup {
|
|
|
207
218
|
}
|
|
208
219
|
}
|
|
209
220
|
|
|
221
|
+
/**
|
|
222
|
+
*
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
export interface MenuBarItem {
|
|
226
|
+
text: string;
|
|
227
|
+
icon?: string;
|
|
228
|
+
disabled?: boolean;
|
|
229
|
+
|
|
230
|
+
/** its menu; a function is called at each opening: it gives the items of that time */
|
|
231
|
+
items: MenuElement[] | ( ( ) => MenuElement[] );
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface MenuBarProps extends Omit<BoxProps,"content"> {
|
|
235
|
+
items: MenuBarItem[];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* a title of the bar
|
|
240
|
+
*/
|
|
241
|
+
|
|
242
|
+
class CMenuBarItem extends Component {
|
|
243
|
+
static classPrefix = "x4";
|
|
244
|
+
|
|
245
|
+
constructor( itm: MenuBarItem ) {
|
|
246
|
+
super( { disabled: itm.disabled } );
|
|
247
|
+
|
|
248
|
+
this.setContent( [
|
|
249
|
+
itm.icon ? new Icon( { id: "icon", iconId: itm.icon } ) : null,
|
|
250
|
+
new Component( { id: "text", content: itm.text } ),
|
|
251
|
+
] );
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* A row of titles, each one opens its menu under it.
|
|
257
|
+
* A click on a title opens its menu, a second one closes it. While a menu is open, the
|
|
258
|
+
* mouse on another title opens the menu of that one.
|
|
259
|
+
*
|
|
260
|
+
* For a web application. A desktop application (Electron...) should not use it: the menu
|
|
261
|
+
* of the system is preferable, where the user expects it (the top of the screen on a Mac)
|
|
262
|
+
* and with the keyboard of the system.
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* new MenuBar( { items: [
|
|
266
|
+
* { text: "File", items: [ { text: "Open…", shortcut: "Mod+O", click: open }, "-", { text: "Quit", click: quit } ] },
|
|
267
|
+
* { text: "Edit", items: ( ) => [ { text: "Undo", disabled: !canUndo( ), click: undo } ] },
|
|
268
|
+
* ] } );
|
|
269
|
+
*
|
|
270
|
+
* @cssvar
|
|
271
|
+
* ```
|
|
272
|
+
* --menubar-background
|
|
273
|
+
* --menubar-border
|
|
274
|
+
* --menubaritem-color
|
|
275
|
+
* --menubaritem-background-hover
|
|
276
|
+
* --menubaritem-color-hover
|
|
277
|
+
* --menubaritem-color-disabled
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
|
|
281
|
+
export class MenuBar extends HBox<MenuBarProps> {
|
|
282
|
+
static classPrefix = "x4";
|
|
283
|
+
|
|
284
|
+
private _menu: Menu; // the menu open, null when none
|
|
285
|
+
private _title: CMenuBarItem; // the title of that menu
|
|
286
|
+
private _closed: CMenuBarItem; // the title whose menu closed last
|
|
287
|
+
private _closedAt = -1; // when (performance.now)
|
|
288
|
+
|
|
289
|
+
constructor( props: MenuBarProps ) {
|
|
290
|
+
super( props );
|
|
291
|
+
|
|
292
|
+
this.setContent( props.items.map( itm => {
|
|
293
|
+
const title = new CMenuBarItem( itm );
|
|
294
|
+
|
|
295
|
+
// The menu closes by itself as soon as the button goes down outside of it,
|
|
296
|
+
// before the title hears of it: it was the menu of this title if it closed
|
|
297
|
+
// since this very press began.
|
|
298
|
+
let wasOpen = false;
|
|
299
|
+
|
|
300
|
+
title.addDOMEvent( "pointerdown", ev => { wasOpen = this._closed===title && this._closedAt >= ev.timeStamp; } );
|
|
301
|
+
title.addDOMEvent( "click", ( ) => {
|
|
302
|
+
if( !wasOpen ) {
|
|
303
|
+
this._open( title, itm );
|
|
304
|
+
}
|
|
305
|
+
} );
|
|
306
|
+
|
|
307
|
+
title.addDOMEvent( "mouseenter", ( ) => {
|
|
308
|
+
if( this._menu && this._title!==title ) {
|
|
309
|
+
this._open( title, itm );
|
|
310
|
+
}
|
|
311
|
+
} );
|
|
312
|
+
|
|
313
|
+
return title;
|
|
314
|
+
} ) );
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* closes the menu open, opens the one of the title under it
|
|
319
|
+
*/
|
|
320
|
+
|
|
321
|
+
private _open( title: CMenuBarItem, itm: MenuBarItem ) {
|
|
322
|
+
if( title.isDisabled( ) ) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if( this._menu ) {
|
|
327
|
+
this._menu.dismiss( );
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const menu = new Menu( { items: isFunction( itm.items ) ? itm.items( ) : itm.items } );
|
|
331
|
+
|
|
332
|
+
menu.on( "closed", ( ) => {
|
|
333
|
+
title.removeClass( "opened" );
|
|
334
|
+
|
|
335
|
+
this._closed = title;
|
|
336
|
+
this._closedAt = performance.now( );
|
|
337
|
+
|
|
338
|
+
if( this._menu===menu ) {
|
|
339
|
+
this._menu = null;
|
|
340
|
+
this._title = null;
|
|
341
|
+
}
|
|
342
|
+
} );
|
|
343
|
+
|
|
344
|
+
this._menu = menu;
|
|
345
|
+
this._title = title;
|
|
346
|
+
title.addClass( "opened" );
|
|
347
|
+
|
|
348
|
+
menu.displayNear( title.getBoundingRect( ), "top left", "bottom left" );
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor"><!--!Font Awesome Free 6.6.0 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
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor"><!--!Font Awesome Free 6.6.0 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 512z"/><path class="mark" d="M256 128c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" fill="currentColor"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" fill="currentColor"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576z"/><path class="mark" d="M320 240C302.3 240 288 254.3 288 272C288 285.3 277.3 296 264 296C250.7 296 240 285.3 240 272C240 227.8 275.8 192 320 192C364.2 192 400 227.8 400 272C400 319.2 364 339.2 344 346.5L344 350.3C344 363.6 333.3 374.3 320 374.3C306.7 374.3 296 363.6 296 350.3L296 342.2C296 321.7 310.8 307 326.1 302C332.5 299.9 339.3 296.5 344.3 291.7C348.6 287.5 352 281.7 352 272.1C352 254.4 337.7 240.1 320 240.1zM288 432C288 414.3 302.3 400 320 400C337.7 400 352 414.3 352 432C352 449.7 337.7 464 320 464C302.3 464 288 449.7 288 432z"/></svg>
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
--msgbox-icon-color: var( --alert-background );
|
|
23
23
|
--msgbox-question-icon-color: var( --accent-background ); // a question, an InputBox, a PromptBox
|
|
24
24
|
|
|
25
|
+
// the "!" and the "?" in the disc of the icon: as the text on the red, on the accent
|
|
26
|
+
--msgbox-icon-mark-color: var( --alert-color );
|
|
27
|
+
--msgbox-question-icon-mark-color: var( --accent-color );
|
|
28
|
+
|
|
25
29
|
--msgbox-log-background: var( --background-ternary ); // the lines of a ProgressionBox
|
|
26
30
|
--msgbox-log-border: var( --border );
|
|
27
31
|
--msgbox-log-border-focus: var( --border-focus );
|
|
@@ -44,6 +48,22 @@
|
|
|
44
48
|
}
|
|
45
49
|
}
|
|
46
50
|
|
|
51
|
+
// the buttons of an error in the red of its title bar: a filled one, the focus of an outline
|
|
52
|
+
// one (its text stays the one of the text: a red text is not read on a dark background)
|
|
53
|
+
&.error {
|
|
54
|
+
--button-background: var( --alert-background );
|
|
55
|
+
--button-background-hover: var( --alert-background-active );
|
|
56
|
+
--button-background-focus: var( --alert-background-active );
|
|
57
|
+
--button-background-active: var( --alert-background-active );
|
|
58
|
+
--button-color: var( --alert-color );
|
|
59
|
+
--button-color-hover: var( --alert-color );
|
|
60
|
+
--button-color-focus: var( --alert-color );
|
|
61
|
+
--button-color-active: var( --alert-color );
|
|
62
|
+
|
|
63
|
+
--ol-button-border-focus: var( --alert-background );
|
|
64
|
+
--ol-button-color-focus: var( --ol-button-color );
|
|
65
|
+
}
|
|
66
|
+
|
|
47
67
|
& .x4form {
|
|
48
68
|
min-width: 400px;
|
|
49
69
|
|
|
@@ -51,6 +71,10 @@
|
|
|
51
71
|
> .x4hbox > .x4icon {
|
|
52
72
|
color: var( --msgbox-icon-color );
|
|
53
73
|
height: 96px;
|
|
74
|
+
|
|
75
|
+
.mark {
|
|
76
|
+
fill: var( --msgbox-icon-mark-color );
|
|
77
|
+
}
|
|
54
78
|
}
|
|
55
79
|
|
|
56
80
|
.x4label {
|
|
@@ -61,6 +85,10 @@
|
|
|
61
85
|
|
|
62
86
|
&.question .x4form > .x4hbox > .x4icon {
|
|
63
87
|
color: var( --msgbox-question-icon-color );
|
|
88
|
+
|
|
89
|
+
.mark {
|
|
90
|
+
fill: var( --msgbox-question-icon-mark-color );
|
|
91
|
+
}
|
|
64
92
|
}
|
|
65
93
|
}
|
|
66
94
|
|
|
@@ -37,6 +37,8 @@ export interface MessageBoxProps extends DialogProps {
|
|
|
37
37
|
* --msgbox-caption-color
|
|
38
38
|
* --msgbox-icon-color
|
|
39
39
|
* --msgbox-question-icon-color
|
|
40
|
+
* --msgbox-icon-mark-color
|
|
41
|
+
* --msgbox-question-icon-mark-color
|
|
40
42
|
* --msgbox-log-background
|
|
41
43
|
* --msgbox-log-border
|
|
42
44
|
* --msgbox-log-border-focus
|
|
@@ -47,7 +49,11 @@ export interface MessageBoxProps extends DialogProps {
|
|
|
47
49
|
* --msgbox-log-scrollbar
|
|
48
50
|
* ```
|
|
49
51
|
* --msgbox-caption-* and --msgbox-icon-color are the ones of an error; a question, an InputBox
|
|
50
|
-
* and a PromptBox have the title bar of a Dialog and their icon in --msgbox-question-icon-color
|
|
52
|
+
* and a PromptBox have the title bar of a Dialog and their icon in --msgbox-question-icon-color.
|
|
53
|
+
* The "!" of an error and the "?" of a question are drawn in the disc of their icon, in
|
|
54
|
+
* --msgbox-icon-mark-color and --msgbox-question-icon-mark-color.
|
|
55
|
+
* The buttons of an error are in the colours of the alerts (--alert-*): a filled one, the border
|
|
56
|
+
* of an outline one that has the focus
|
|
51
57
|
*/
|
|
52
58
|
|
|
53
59
|
export class MessageBox extends Dialog<DialogProps>
|
|
@@ -54,7 +54,10 @@
|
|
|
54
54
|
color: var( --notification-icon-default );
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// a box does not shrink (.x4box): this one does, down to the frame, its close button kept in it
|
|
57
58
|
.body {
|
|
59
|
+
flex-shrink: 1;
|
|
60
|
+
|
|
58
61
|
.title {
|
|
59
62
|
font-weight: bold;
|
|
60
63
|
padding: 6px 6px 3px 6px;
|
|
@@ -64,6 +67,7 @@
|
|
|
64
67
|
.text {
|
|
65
68
|
padding: 3px 6px 6px 6px;
|
|
66
69
|
color: var( --notification-text-color );
|
|
70
|
+
overflow-wrap: anywhere; // a long word, an address: cut at the edge
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
|
|
@@ -49,17 +49,31 @@ function getRoot( ) {
|
|
|
49
49
|
/**
|
|
50
50
|
* on the document while the event goes down (capture): a press outside
|
|
51
51
|
* every popup closes the group opened last, the others at the next presses.
|
|
52
|
+
* A press in a popup closes the groups opened after it, over it (a context
|
|
53
|
+
* menu, the picker of a ColorInput), not its own.
|
|
52
54
|
* The event is left as it is, what is pressed handles it as usual.
|
|
53
55
|
* One function for all the popups: it is removed by the one that empties the list.
|
|
54
56
|
*/
|
|
55
57
|
|
|
56
58
|
function dismissLast( e: UIEvent ) {
|
|
57
|
-
|
|
59
|
+
const target = e.target as Node;
|
|
60
|
+
|
|
61
|
+
// the newest popup the press is in
|
|
62
|
+
let idx = autoclose_list.length-1;
|
|
63
|
+
while( idx>=0 && !autoclose_list[idx].contains( target ) ) {
|
|
64
|
+
idx--;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// outside every popup: the newest one; dismiss( ) closes its whole group (a menu and its sub-menus)
|
|
68
|
+
if( idx<0 ) {
|
|
69
|
+
autoclose_list[autoclose_list.length-1].dismiss( );
|
|
58
70
|
return;
|
|
59
71
|
}
|
|
60
72
|
|
|
61
|
-
//
|
|
62
|
-
autoclose_list[
|
|
73
|
+
// in a popup: the groups opened after it, not its own (a menu and its sub-menus)
|
|
74
|
+
const group = autoclose_list[idx].getData( "close" );
|
|
75
|
+
const over = autoclose_list.slice( idx+1 ).filter( x => x.getData( "close" )!==group );
|
|
76
|
+
over.reverse( ).forEach( x => x.dismiss( ) );
|
|
63
77
|
}
|
|
64
78
|
|
|
65
79
|
/**
|
|
@@ -395,6 +409,11 @@ export class Popup<P extends PopupProps = PopupProps, E extends PopupEvents = Po
|
|
|
395
409
|
let aidx = -1;
|
|
396
410
|
if( after ) {
|
|
397
411
|
aidx = autoclose_list.indexOf( this );
|
|
412
|
+
|
|
413
|
+
// no longer open: none was opened after it
|
|
414
|
+
if( aidx<0 ) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
398
417
|
}
|
|
399
418
|
|
|
400
419
|
autoclose_list.forEach( (x,idx) => {
|
|
@@ -22,9 +22,10 @@
|
|
|
22
22
|
|
|
23
23
|
--radio-button-border: var( --border );
|
|
24
24
|
--radio-button-color: var( --text-ternary );
|
|
25
|
-
--radio-button-color-checked: var( --accent-
|
|
25
|
+
--radio-button-color-checked: var( --accent-text );
|
|
26
26
|
--radio-button-background-checked: var( --background-secondary );
|
|
27
27
|
--radio-button-border-checked: var( --accent-background );
|
|
28
|
+
--radio-button-icon-size: 24px; // set by a class of the application on a radio: it wins over :root
|
|
28
29
|
|
|
29
30
|
--radio-danger-color: var( --alert-background );
|
|
30
31
|
--radio-danger-background: var( --alert-background-light );
|
|
@@ -132,8 +133,6 @@
|
|
|
132
133
|
// square: its former name, kept for the code that has it (not documented)
|
|
133
134
|
.x4radio.button,
|
|
134
135
|
.x4radio.square {
|
|
135
|
-
--size: 24px;
|
|
136
|
-
|
|
137
136
|
&.outline {
|
|
138
137
|
border-radius: 22px;;
|
|
139
138
|
}
|
|
@@ -159,8 +158,8 @@
|
|
|
159
158
|
#icon {
|
|
160
159
|
fill: var( --radio-button-color );
|
|
161
160
|
color: var( --radio-button-color );
|
|
162
|
-
width: var( --size );
|
|
163
|
-
height: var( --size );
|
|
161
|
+
width: var( --radio-button-icon-size );
|
|
162
|
+
height: var( --radio-button-icon-size );
|
|
164
163
|
}
|
|
165
164
|
}
|
|
166
165
|
|
|
@@ -388,22 +388,29 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
388
388
|
}
|
|
389
389
|
})
|
|
390
390
|
|
|
391
|
+
// the cell of the data under the mouse; undefined out of them
|
|
391
392
|
const targetCell = (e: MouseEvent) => {
|
|
392
393
|
let el = e.target as Element;
|
|
393
394
|
while (el && !el.classList.contains("cell")) {
|
|
394
395
|
el = el.parentElement;
|
|
395
396
|
}
|
|
396
397
|
|
|
397
|
-
if (el) {
|
|
398
|
-
|
|
399
|
-
return {
|
|
400
|
-
ref: cel.getIntData("ref"),
|
|
401
|
-
row: cel.getInternalData("row"),
|
|
402
|
-
col: cel.getInternalData("col"),
|
|
403
|
-
}
|
|
398
|
+
if (!el) {
|
|
399
|
+
return undefined;
|
|
404
400
|
}
|
|
405
401
|
|
|
406
|
-
|
|
402
|
+
// a cell of the header or of the footer (a title, the sizer of a column): no row
|
|
403
|
+
const cel = componentFromDOM(el);
|
|
404
|
+
const row = cel.getInternalData("row");
|
|
405
|
+
if (row === undefined) {
|
|
406
|
+
return undefined;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
ref: cel.getIntData("ref"),
|
|
411
|
+
row,
|
|
412
|
+
col: cel.getInternalData("col"),
|
|
413
|
+
}
|
|
407
414
|
}
|
|
408
415
|
|
|
409
416
|
|
|
@@ -416,11 +423,11 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
416
423
|
// click on another cell while editing:
|
|
417
424
|
// validate now, rows may be rebuilt before the click event (and the click lost)
|
|
418
425
|
const ref = targetCell(e);
|
|
419
|
-
if( ref
|
|
426
|
+
if( ref ) {
|
|
420
427
|
this._endEdit( true, true );
|
|
421
428
|
|
|
422
429
|
if (!this._selection.has(ref.ref)) {
|
|
423
|
-
this.
|
|
430
|
+
this.clearSelection( false );
|
|
424
431
|
this._addSelection(ref.ref,true);
|
|
425
432
|
}
|
|
426
433
|
|
|
@@ -438,7 +445,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
438
445
|
if (ref) {
|
|
439
446
|
//TODO: multiselection
|
|
440
447
|
if (!this._selection.has(ref.ref)) {
|
|
441
|
-
this.
|
|
448
|
+
this.clearSelection( false );
|
|
442
449
|
this._addSelection(ref.ref,true);
|
|
443
450
|
}
|
|
444
451
|
}
|
|
@@ -454,7 +461,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
454
461
|
if (ref) {
|
|
455
462
|
//TODO: multiselection
|
|
456
463
|
if (!this._selection.has(ref.ref)) {
|
|
457
|
-
this.
|
|
464
|
+
this.clearSelection(false);
|
|
458
465
|
this._addSelection(ref.ref,true);
|
|
459
466
|
}
|
|
460
467
|
|
|
@@ -479,7 +486,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
479
486
|
if (ref) {
|
|
480
487
|
//TODO: multiselection
|
|
481
488
|
if (!this._selection.has(ref.ref)) {
|
|
482
|
-
this.
|
|
489
|
+
this.clearSelection( false );
|
|
483
490
|
this._addSelection(ref.ref, true );
|
|
484
491
|
}
|
|
485
492
|
|
|
@@ -638,7 +645,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
638
645
|
|
|
639
646
|
if (sens == kbNav.first || sens == kbNav.last) {
|
|
640
647
|
let nline = sens == kbNav.first ? 0 : this._store.getRowCount() - 1;
|
|
641
|
-
this.
|
|
648
|
+
this.clearSelection(false);
|
|
642
649
|
this._addSelection(mkid(nline,0), true);
|
|
643
650
|
this._scrollToIndex(nline);
|
|
644
651
|
return true;
|
|
@@ -649,7 +656,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
649
656
|
|
|
650
657
|
let nline = sens == kbNav.next ? fline + 1 : fline - 1;
|
|
651
658
|
if (nline >= 0 && nline < this._store.getRowCount()) {
|
|
652
|
-
this.
|
|
659
|
+
this.clearSelection(false);
|
|
653
660
|
this._addSelection( mkid(nline,col), true);
|
|
654
661
|
this._scrollToIndex( nline );
|
|
655
662
|
return true;
|
|
@@ -670,7 +677,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
670
677
|
}
|
|
671
678
|
|
|
672
679
|
if (nline != fline) {
|
|
673
|
-
this.
|
|
680
|
+
this.clearSelection( false );
|
|
674
681
|
this._addSelection(mkid(nline,col), true);
|
|
675
682
|
|
|
676
683
|
if (this._store.getRowCount() < SCROLL_LIMIT) {
|
|
@@ -686,7 +693,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
686
693
|
|
|
687
694
|
let ncol = sens == kbNav.right ? col+1 : col-1;
|
|
688
695
|
if (ncol >= 0 && ncol < this._columns.length ) {
|
|
689
|
-
this.
|
|
696
|
+
this.clearSelection(false);
|
|
690
697
|
this._addSelection( mkid(fline,ncol), true);
|
|
691
698
|
|
|
692
699
|
// fixed columns are always visible
|
|
@@ -1374,10 +1381,10 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
1374
1381
|
}
|
|
1375
1382
|
}
|
|
1376
1383
|
/**
|
|
1377
|
-
*
|
|
1384
|
+
* no cell selected; notify: selectionChange is fired, its selection empty
|
|
1378
1385
|
*/
|
|
1379
1386
|
|
|
1380
|
-
|
|
1387
|
+
clearSelection(notify = true) {
|
|
1381
1388
|
for (const ref of this._selection.keys()) {
|
|
1382
1389
|
const els = this.queryAll(`.cell[data-ref="${ref}"]`)
|
|
1383
1390
|
els.forEach(el => {
|
|
@@ -1433,7 +1440,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
1433
1440
|
|
|
1434
1441
|
selectItem(row: number, col: number, append = false) {
|
|
1435
1442
|
if (!append) {
|
|
1436
|
-
this.
|
|
1443
|
+
this.clearSelection(false);
|
|
1437
1444
|
}
|
|
1438
1445
|
|
|
1439
1446
|
this._addSelection(mkid(row, col), true);
|
|
@@ -1745,7 +1752,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
1745
1752
|
}
|
|
1746
1753
|
|
|
1747
1754
|
if( this._selection.size!=1 || !this._selection.has(ref) ) {
|
|
1748
|
-
this.
|
|
1755
|
+
this.clearSelection( false );
|
|
1749
1756
|
this._addSelection( ref, true );
|
|
1750
1757
|
}
|
|
1751
1758
|
|
|
@@ -88,7 +88,10 @@ export class TextEdit<E extends ComponentEvents = ComponentEvents> extends HBox<
|
|
|
88
88
|
|
|
89
89
|
constructor( props: TextEditProps ) {
|
|
90
90
|
// dom_events are for the input (focus, blur, keydown...): not on the box too,
|
|
91
|
-
// where keydown would run a second time, and focus never (it does not bubble)
|
|
91
|
+
// where keydown would run a second time, and focus never (it does not bubble).
|
|
92
|
+
// They are set when the input is made, before the handlers of Input and of the class
|
|
93
|
+
// (the keys of an AutoComplete): they run first. To run after them, add a handler to
|
|
94
|
+
// getInput( ) once the component is made.
|
|
92
95
|
super( { ...props, dom_events: undefined } );
|
|
93
96
|
|
|
94
97
|
if( !props.inputId ) {
|