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
|
@@ -88,6 +88,9 @@ $color--dark-base: #2d6fa5;
|
|
|
88
88
|
--accent-color-focus: var(--color-primary-a0);
|
|
89
89
|
--accent-color-active: var(--color-primary-a0);
|
|
90
90
|
|
|
91
|
+
// the accent as the colour of a text, on the background: a button checked, focused
|
|
92
|
+
--accent-text: var(--accent-background-active);
|
|
93
|
+
|
|
91
94
|
// Borders
|
|
92
95
|
--border: color-mix(in srgb, var(--color-primary-a100) 11.8%, var(--color-primary-a0)); // #e1e1e1
|
|
93
96
|
--border-light: color-mix(in srgb, var(--color-primary-a100) 8.2%, var(--color-primary-a0)); // #eaeaea
|
|
@@ -162,6 +165,14 @@ html.x4theme-dark {
|
|
|
162
165
|
--accent-color-focus: #ffffff;
|
|
163
166
|
--accent-color-active: #ffffff;
|
|
164
167
|
|
|
168
|
+
// the accent as a text: the blues of the scale are dark, lost on the background; a light one (chosen)
|
|
169
|
+
--accent-text: #{color.adjust($color--dark-base, $lightness: 25%)};
|
|
170
|
+
|
|
171
|
+
// the edge of a button checked or focused, in that blue: the dark ones are lost on the background
|
|
172
|
+
--checkbox-button-border-checked: var( --accent-text );
|
|
173
|
+
--radio-button-border-checked: var( --accent-text );
|
|
174
|
+
--ol-button-border-focus: var( --accent-text );
|
|
175
|
+
|
|
165
176
|
// the title bar of a Dialog: the one of the window of VS Code
|
|
166
177
|
--color-dialog-caption: #181818; // titleBar.activeBackground
|
|
167
178
|
--color-dialog-caption-text: #cccccc; // titleBar.activeForeground
|
|
@@ -177,5 +188,8 @@ html.x4theme-dark {
|
|
|
177
188
|
// the marks of the events out of the view of a Planner: the darkest blue (a90 is the text here)
|
|
178
189
|
--planner-edge-background: var( --accent-background-active );
|
|
179
190
|
|
|
191
|
+
// the icon of a tooltip: the dark blues are lost on its background; the colour of the descriptions
|
|
192
|
+
--tooltip-icon-color: var( --text-secondary );
|
|
193
|
+
|
|
180
194
|
color-scheme: dark; // the native parts: select, scroll bars, number spinners
|
|
181
195
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { asap } from '../../core/core_tools';
|
|
18
18
|
import { EventCallback } from '../../core/core_events';
|
|
19
|
-
import { Component, ComponentEvent, ComponentEvents, ComponentProps, EvClick, EvDblClick, EvSelectionChange } from '../../core/component';
|
|
19
|
+
import { Component, ComponentEvent, ComponentEvents, ComponentProps, EvClick, EvContextMenu, EvDblClick, EvSelectionChange } from '../../core/component';
|
|
20
20
|
|
|
21
21
|
import { ScrollView, Viewport } from '../viewport/viewport';
|
|
22
22
|
import { Label, SimpleText } from '../label/label';
|
|
@@ -59,12 +59,14 @@ export interface TreeviewProps extends Omit<ComponentProps,"content"> {
|
|
|
59
59
|
selectionChange?: EventCallback<EvSelectionChange>;
|
|
60
60
|
dblClick?: EventCallback<EvDblClick>;
|
|
61
61
|
click?: EventCallback<EvClick>;
|
|
62
|
+
contextMenu?: EventCallback<EvContextMenu>;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
export interface TreeviewEvents extends ComponentEvents {
|
|
65
66
|
selectionChange?: EvSelectionChange;
|
|
66
67
|
dblClick?: EvDblClick;
|
|
67
68
|
click?: EvClick;
|
|
69
|
+
contextMenu?: EvContextMenu;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
/**
|
|
@@ -185,7 +187,15 @@ class CTreeViewItem extends Box {
|
|
|
185
187
|
|
|
186
188
|
|
|
187
189
|
/**
|
|
188
|
-
*
|
|
190
|
+
* A tree of items, folders that open and close.
|
|
191
|
+
*
|
|
192
|
+
* A right click selects the item under the mouse, then fires contextMenu, its context the
|
|
193
|
+
* id of the item (null out of the items); the menu of the browser is not shown.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* new Treeview( { items, contextMenu: ( ev ) => {
|
|
197
|
+
* new Menu( { items: [ { text: "Rename", click: ( ) => rename( ev.context ) } ] } ).displayAt( ev.uievent.clientX, ev.uievent.clientY );
|
|
198
|
+
* } } );
|
|
189
199
|
*/
|
|
190
200
|
|
|
191
201
|
export class Treeview extends Component<TreeviewProps,TreeviewEvents> {
|
|
@@ -199,7 +209,7 @@ export class Treeview extends Component<TreeviewProps,TreeviewEvents> {
|
|
|
199
209
|
constructor( props: TreeviewProps ) {
|
|
200
210
|
super( props );
|
|
201
211
|
|
|
202
|
-
this.mapPropEvents( props, "selectionChange", "dblClick", "click" );
|
|
212
|
+
this.mapPropEvents( props, "selectionChange", "dblClick", "click", "contextMenu" );
|
|
203
213
|
|
|
204
214
|
const scroller = new ScrollView( { cls: "body" } );
|
|
205
215
|
this._view = scroller.getViewport( );
|
|
@@ -218,6 +228,7 @@ export class Treeview extends Component<TreeviewProps,TreeviewEvents> {
|
|
|
218
228
|
this.setDOMEvents( {
|
|
219
229
|
click: ( ev ) => this._on_click( ev ),
|
|
220
230
|
dblclick: (e) => this._on_click(e),
|
|
231
|
+
contextmenu: ( ev ) => this._on_ctx_menu( ev ),
|
|
221
232
|
keydown: ( ev ) => this._onkey( ev ),
|
|
222
233
|
});
|
|
223
234
|
|
|
@@ -244,10 +255,18 @@ export class Treeview extends Component<TreeviewProps,TreeviewEvents> {
|
|
|
244
255
|
}
|
|
245
256
|
}
|
|
246
257
|
|
|
258
|
+
/**
|
|
259
|
+
* the item of this tree an element is in, not one of a component around it; null when none
|
|
260
|
+
*/
|
|
261
|
+
|
|
262
|
+
private _itemOf( target: EventTarget ): Component {
|
|
263
|
+
const c = Component.closest( target as Element, ".item" );
|
|
264
|
+
return c && this.contains( c ) ? c : null;
|
|
265
|
+
}
|
|
266
|
+
|
|
247
267
|
private _on_click( ev: UIEvent ) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if( c && this.contains( c ) ) {
|
|
268
|
+
const c = this._itemOf( ev.target );
|
|
269
|
+
if( c ) {
|
|
251
270
|
const id = c.getInternalData( "id" );
|
|
252
271
|
|
|
253
272
|
const fev: ComponentEvent = { context:id };
|
|
@@ -268,6 +287,25 @@ export class Treeview extends Component<TreeviewProps,TreeviewEvents> {
|
|
|
268
287
|
this.clearSelection( );
|
|
269
288
|
}
|
|
270
289
|
|
|
290
|
+
/**
|
|
291
|
+
* a right click: the item under the mouse selected, then contextMenu
|
|
292
|
+
*/
|
|
293
|
+
|
|
294
|
+
private _on_ctx_menu( ev: MouseEvent ) {
|
|
295
|
+
ev.preventDefault( );
|
|
296
|
+
|
|
297
|
+
const c = this._itemOf( ev.target );
|
|
298
|
+
if( c ) {
|
|
299
|
+
const id = c.getInternalData( "id" );
|
|
300
|
+
|
|
301
|
+
this._selectItem( id, c );
|
|
302
|
+
this.fire( "contextMenu", { uievent: ev, context: id } );
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
this.fire( "contextMenu", { uievent: ev, context: null } );
|
|
307
|
+
}
|
|
308
|
+
|
|
271
309
|
private _onkey( ev: KeyboardEvent ) {
|
|
272
310
|
switch( ev.key ) {
|
|
273
311
|
case "ArrowDown": {
|
|
@@ -47,6 +47,19 @@ export interface ClipboardRead extends ClipboardContent {
|
|
|
47
47
|
/** what the application set last */
|
|
48
48
|
let kept: ClipboardContent = null;
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* The image to read among the types of the clipboard: the PNG when there is one, every
|
|
52
|
+
* program reads it; else the first image (a program puts its own format first: a SVG).
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
function imageTypeOf( types: readonly string[] ): string {
|
|
56
|
+
if( types.includes( "image/png" ) ) {
|
|
57
|
+
return "image/png";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return types.find( type => type.startsWith( "image/" ) );
|
|
61
|
+
}
|
|
62
|
+
|
|
50
63
|
function toPng( image: Blob ): Promise<Blob> {
|
|
51
64
|
return new Promise( ( resolve, reject ) => {
|
|
52
65
|
const url = URL.createObjectURL( image );
|
|
@@ -119,8 +132,9 @@ export async function setClipboard( data: ClipboardContent ): Promise<boolean> {
|
|
|
119
132
|
/**
|
|
120
133
|
* Read the clipboard, from a menu or a button.
|
|
121
134
|
*
|
|
122
|
-
* @returns what the system holds
|
|
123
|
-
* the application set last. Never files: see
|
|
135
|
+
* @returns what the system holds, its image as a PNG when it has one. When it
|
|
136
|
+
* refuses or cannot be asked, what the application set last. Never files: see
|
|
137
|
+
* getEventClipboard.
|
|
124
138
|
*
|
|
125
139
|
* @example
|
|
126
140
|
* const data = await getClipboard( );
|
|
@@ -143,12 +157,14 @@ export async function getClipboard( ): Promise<ClipboardRead> {
|
|
|
143
157
|
const data: ClipboardRead = { files: [] };
|
|
144
158
|
|
|
145
159
|
for( const item of await system.read( ) ) {
|
|
160
|
+
const imageType = imageTypeOf( item.types );
|
|
161
|
+
|
|
146
162
|
for( const type of item.types ) {
|
|
147
163
|
if( type==="text/plain" && !data.text ) {
|
|
148
164
|
const blob = await item.getType( type );
|
|
149
165
|
data.text = ( await blob.text( ) ) || undefined;
|
|
150
166
|
}
|
|
151
|
-
else if( type
|
|
167
|
+
else if( type===imageType && !data.image ) {
|
|
152
168
|
data.image = await item.getType( type );
|
|
153
169
|
}
|
|
154
170
|
}
|
|
@@ -212,8 +228,9 @@ export function setEventClipboard( ev: ClipboardEvent, data: ClipboardContent ):
|
|
|
212
228
|
}
|
|
213
229
|
|
|
214
230
|
/**
|
|
215
|
-
* Read the clipboard in a "paste" event
|
|
216
|
-
* as it is: prevent it when what was
|
|
231
|
+
* Read the clipboard in a "paste" event, its image the PNG file when there is
|
|
232
|
+
* one. The default of the event is left as it is: prevent it when what was
|
|
233
|
+
* read is used.
|
|
217
234
|
*
|
|
218
235
|
* @example
|
|
219
236
|
* this.addDOMEvent( "paste", ev => {
|
|
@@ -232,7 +249,9 @@ export function getEventClipboard( ev: ClipboardEvent ): ClipboardRead {
|
|
|
232
249
|
if( transfer ) {
|
|
233
250
|
data.text = transfer.getData( "text/plain" ) || undefined;
|
|
234
251
|
data.files = Array.from( transfer.files );
|
|
235
|
-
|
|
252
|
+
|
|
253
|
+
const imageType = imageTypeOf( data.files.map( file => file.type ) );
|
|
254
|
+
data.image = data.files.find( file => file.type===imageType );
|
|
236
255
|
}
|
|
237
256
|
|
|
238
257
|
return data;
|