x4js 2.3.24 → 2.3.26

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.
Files changed (68) hide show
  1. package/aicontext.md +101 -17
  2. package/aiguide.md +64 -15
  3. package/package.json +1 -1
  4. package/src/components/assets/icons.ts +2 -0
  5. package/src/components/autocomplete/autocomplete.ts +2 -2
  6. package/src/components/boxes/boxes.module.scss +16 -2
  7. package/src/components/boxes/boxes.ts +24 -11
  8. package/src/components/breadcrumb/breadcrumb.ts +1 -2
  9. package/src/components/btngroup/btngroup.ts +2 -2
  10. package/src/components/button/button.ts +7 -2
  11. package/src/components/calendar/calendar.ts +4 -2
  12. package/src/components/canvas/canvas.ts +1 -2
  13. package/src/components/chart/chart.ts +3 -3
  14. package/src/components/checkbox/checkbox.ts +2 -2
  15. package/src/components/colorinput/colorinput.ts +3 -2
  16. package/src/components/colorpicker/colorpicker.ts +5 -5
  17. package/src/components/combobox/combobox.ts +3 -3
  18. package/src/components/dialog/dialog.ts +8 -2
  19. package/src/components/dockingview/dockingview.ts +1 -2
  20. package/src/components/filedrop/filedrop.ts +3 -2
  21. package/src/components/form/form.module.scss +2 -1
  22. package/src/components/form/form.ts +7 -3
  23. package/src/components/gauge/gauge.ts +2 -2
  24. package/src/components/gridview/gridview.module.scss +8 -5
  25. package/src/components/gridview/gridview.ts +11 -13
  26. package/src/components/header/header.ts +2 -2
  27. package/src/components/icon/icon.ts +1 -2
  28. package/src/components/image/image.ts +1 -2
  29. package/src/components/input/input.ts +3 -2
  30. package/src/components/keyboard/keyboard.ts +2 -2
  31. package/src/components/label/label.ts +3 -3
  32. package/src/components/link/link.ts +3 -2
  33. package/src/components/listbox/listbox.module.scss +59 -2
  34. package/src/components/listbox/listbox.ts +133 -23
  35. package/src/components/menu/menu.ts +5 -4
  36. package/src/components/messages/messages.module.scss +95 -3
  37. package/src/components/messages/messages.ts +113 -27
  38. package/src/components/notification/notification.ts +6 -6
  39. package/src/components/panel/panel.ts +2 -2
  40. package/src/components/popup/popup.ts +17 -2
  41. package/src/components/progress/progress.ts +1 -2
  42. package/src/components/propgrid/progrid.module.scss +2 -3
  43. package/src/components/propgrid/propgrid.ts +32 -38
  44. package/src/components/radio/radio.ts +1 -2
  45. package/src/components/rating/rating.ts +2 -2
  46. package/src/components/select/select.ts +2 -2
  47. package/src/components/settingsview/settingsview.module.scss +6 -5
  48. package/src/components/settingsview/settingsview.ts +3 -3
  49. package/src/components/sizers/sizer.ts +21 -29
  50. package/src/components/slider/slider.ts +2 -2
  51. package/src/components/spreadsheet/spreadsheet.ts +8 -10
  52. package/src/components/switch/switch.ts +2 -2
  53. package/src/components/tabs/tabs.ts +4 -4
  54. package/src/components/tag/tag.ts +2 -2
  55. package/src/components/textarea/textarea.ts +3 -3
  56. package/src/components/textedit/textedit.ts +2 -2
  57. package/src/components/themes.scss +2 -0
  58. package/src/components/tickline/tickline.ts +3 -3
  59. package/src/components/tooltips/tooltips.ts +11 -2
  60. package/src/components/treeview/treeview.ts +3 -3
  61. package/src/components/video/video.ts +1 -2
  62. package/src/components/viewport/viewport.ts +4 -3
  63. package/src/core/component.ts +15 -24
  64. package/src/core/core_application.ts +36 -3
  65. package/src/core/core_events.ts +14 -2
  66. package/src/core/core_i18n.ts +67 -0
  67. package/src/core/core_svg.ts +1 -0
  68. package/src/core/core_tools.ts +83 -24
@@ -2,7 +2,7 @@
2
2
  // :: MESSAGEBOX ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
3
3
 
4
4
  import { _tr } from '../../core/core_i18n';
5
- import { asap, class_ns, unsafeHtml, UnsafeHtml } from '../../core/core_tools';
5
+ import { asap, sprintf, unsafeHtml, UnsafeHtml } from '../../core/core_tools';
6
6
 
7
7
  import { HBox, VBox } from '../boxes/boxes';
8
8
  import { Icon } from '../icon/icon';
@@ -11,10 +11,10 @@ import { Dialog, DialogProps } from "../dialog/dialog"
11
11
  import { Form } from '../form/form';
12
12
  import { BtnGroupItem } from '../btngroup/btngroup';
13
13
  import { Input } from '../input/input';
14
- import { SimpleTextArea } from '../textarea/textarea';
15
14
  import { Button } from '../button/button';
16
15
  import { Component } from '../../core/component';
17
16
  import { Progress } from '../components.js';
17
+ import icons from '../assets/icons';
18
18
 
19
19
  import "./messages.module.scss";
20
20
 
@@ -38,14 +38,22 @@ export interface MessageBoxProps extends DialogProps {
38
38
  * --msgbox-icon-color
39
39
  * --msgbox-question-icon-color
40
40
  * --msgbox-log-background
41
+ * --msgbox-log-border
42
+ * --msgbox-log-border-focus
43
+ * --msgbox-log-error-color
44
+ * --msgbox-log-error-background
45
+ * --msgbox-log-warning-color
46
+ * --msgbox-log-warning-background
47
+ * --msgbox-log-scrollbar
41
48
  * ```
42
49
  * --msgbox-caption-* and --msgbox-icon-color are the ones of an error; a question, an InputBox
43
50
  * and a PromptBox have the title bar of a Dialog and their icon in --msgbox-question-icon-color
44
51
  */
45
52
 
46
- @class_ns( "x4" )
47
53
  export class MessageBox extends Dialog<DialogProps>
48
54
  {
55
+ static classPrefix = "x4";
56
+
49
57
  constructor(props: DialogProps) {
50
58
  super(props);
51
59
  }
@@ -134,9 +142,10 @@ interface InputOptions {
134
142
  trim?: boolean;
135
143
  }
136
144
 
137
- @class_ns( "x4" )
138
145
  export class InputBox extends Dialog<DialogProps>
139
146
  {
147
+ static classPrefix = "x4";
148
+
140
149
  constructor(props: DialogProps) {
141
150
  super(props);
142
151
  }
@@ -203,9 +212,10 @@ export class InputBox extends Dialog<DialogProps>
203
212
  // :: PROMPT ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
204
213
 
205
214
 
206
- @class_ns( "x4" )
207
215
  export class PromptBox extends Dialog<DialogProps>
208
216
  {
217
+ static classPrefix = "x4";
218
+
209
219
  constructor(props: DialogProps) {
210
220
  super(props);
211
221
  }
@@ -279,15 +289,21 @@ export class PromptBox extends Dialog<DialogProps>
279
289
 
280
290
  /**
281
291
  * a modal box that follows a long task: a title, a bar, and the lines of what is done
282
- * in a textarea read only (they are text: a markup is shown as it is written).
283
- * done( ) shows its button OK: after an error it waits for it, else it closes the box
284
- * after 5s, the seconds left in the button.
292
+ * in a <pre>, in a fixed font, a <div> each: a string is text, an UnsafeHtml is html. An
293
+ * error, a warning are coloured on all their width, a bar on their left. addSection puts
294
+ * the lines that follow in a section that folds. Ctrl+A selects the lines.
295
+ * done( ) shows its button OK: after an error or a warning it waits for it, their counts
296
+ * on the left of the bar; else it closes the box after 5s, the seconds left in the button.
285
297
  */
286
298
 
287
- @class_ns( "x4" )
288
299
  export class ProgressionBox extends Dialog {
300
+ static classPrefix = "x4";
301
+
302
+ // an error or a warning: done( ) waits for OK
303
+ private _errors = 0;
304
+ private _warnings = 0;
289
305
 
290
- #has_errors = false;
306
+ private _section: Component; // the last section (a <details>): the lines go in it
291
307
 
292
308
  constructor( title: string ) {
293
309
  super( {
@@ -303,13 +319,15 @@ export class ProgressionBox extends Dialog {
303
319
  new VBox( { flex: 1, cls: "right", content: [
304
320
  new SimpleText( { id: "title", text: title } ),
305
321
  new Progress( { id:"prog", min: 0, max: 100, value: 0 } ),
306
- new SimpleTextArea( { id: "sub-text", readonly: true, trim: false } ),
322
+ new Component( { tag: "pre", id: "sub-text", attrs: { tabindex: 0 }, dom_events: {
323
+ keydown: ( ev ) => this._on_log_key( ev ),
324
+ } } ),
307
325
  ]})
308
326
  ]
309
327
  }),
310
328
  ]
311
329
  }),
312
- buttons: [ "ok.outline.default" ]
330
+ buttons: [ new Label( { id: "alerts", text: "" } ), "-", "ok.outline.default" ]
313
331
  });
314
332
 
315
333
  this.query("#btnbar").show( false );
@@ -328,50 +346,118 @@ export class ProgressionBox extends Dialog {
328
346
  this.query<Progress>( "#prog").setValue( perc );
329
347
  }
330
348
 
331
- /** a line at the end: done( ) then waits for the button OK */
349
+ /** a line at the end, with the icon of an error: done( ) then waits for the button OK */
332
350
 
333
351
  addError( text: string | UnsafeHtml, perc: number ) {
334
- this._addLine( text );
352
+ this._addLine( text, "error" );
353
+ this.query<Progress>( "#prog").setValue( perc );
354
+ this.addClass( "error" ); // the title bar in red, as the one of an error of MessageBox
355
+
356
+ this._errors++;
357
+ }
358
+
359
+ /** a line at the end, with the icon of a warning: done( ) then waits for the button OK */
360
+
361
+ addWarning( text: string | UnsafeHtml, perc: number ) {
362
+ this._addLine( text, "warning" );
335
363
  this.query<Progress>( "#prog").setValue( perc );
336
364
 
337
- this.#has_errors = true;
365
+ this._warnings++;
366
+ }
367
+
368
+ /**
369
+ * a section at the end, open: the lines that follow go in it. A click on its title folds
370
+ * it; its title has the icon of the worst of its lines (an error, a warning)
371
+ */
372
+
373
+ addSection( title: string | UnsafeHtml, perc: number ) {
374
+ const summary = new Component( { tag: "summary", content: new Component( { tag: "span", cls: "alert" } ) } );
375
+ summary.appendContent( title );
376
+
377
+ this._section = new Component( { tag: "details", attrs: { open: true }, content: summary } );
378
+
379
+ const log = this.query( "#sub-text" );
380
+ log.appendContent( this._section );
381
+ log.dom.scrollTop = log.dom.scrollHeight;
382
+ this.query<Progress>( "#prog").setValue( perc );
338
383
  }
339
384
 
340
385
  /** the text in place of all the lines */
341
386
 
342
387
  setText( text: string | UnsafeHtml, perc: number ) {
343
- this.query<SimpleTextArea>( "#sub-text" ).setText( text.toString( ) );
388
+ this._section = null;
389
+ this.query( "#sub-text" ).setContent( text );
344
390
  this.query<Progress>( "#prog").setValue( perc );
345
391
  }
346
392
 
347
393
  /** as at the start: no line, no error, no button OK */
348
394
 
349
395
  clear( ) {
350
- this.#has_errors = false;
396
+ this._errors = 0;
397
+ this._warnings = 0;
351
398
  this.clearInterval( "close" );
352
399
  this.query( "#btnbar" ).show( false );
353
- this.query<SimpleTextArea>( "#sub-text" ).setText( "" );
400
+ this.removeClass( "error" );
401
+ this.query<Label>( "#alerts" ).setText( "" );
402
+ this.query<Label>( "#alerts" ).setIcon( null );
403
+ this._section = null;
404
+ this.query( "#sub-text" ).clearContent( );
354
405
  }
355
406
 
356
- // the line after the others, the last one shown
357
- private _addLine( text: string | UnsafeHtml ) {
358
- const log = this.query<SimpleTextArea>( "#sub-text" );
359
- const lines = log.getText( );
407
+ // the line after the others (in the last section), the last one shown: a <div>, of the
408
+ // class of its kind for an error, a warning
409
+ private _addLine( text: string | UnsafeHtml, kind?: "error" | "warning" ) {
410
+ const log = this.query( "#sub-text" );
411
+ const target = this._section ?? log;
412
+
413
+ target.appendContent( new Component( { cls: kind ? "line " + kind : "line", content: text } ) );
414
+
415
+ // the title of the section: the icon of the worst, in its colour
416
+ const alert = this._section?.query( ".alert" );
417
+ if( kind && alert && !alert.hasClass( "error" ) ) {
418
+ alert.setClass( "warning", kind=="warning" );
419
+ alert.setClass( "error", kind=="error" );
420
+ alert.setContent( unsafeHtml( kind=="error" ? icons.danger : icons.warning ) );
421
+ }
422
+
423
+ log.dom.scrollTop = log.dom.scrollHeight;
424
+ }
360
425
 
361
- log.setText( lines ? lines + "\n" + text : text.toString( ) );
362
- log.scrollToBottom( );
426
+ // Ctrl+A: the lines only, not the whole page
427
+ private _on_log_key( ev: KeyboardEvent ) {
428
+ if( ( ev.ctrlKey || ev.metaKey ) && ev.key.toLowerCase( )=="a" ) {
429
+ document.getSelection( ).selectAllChildren( ev.currentTarget as HTMLElement );
430
+ ev.preventDefault( );
431
+ }
363
432
  }
364
433
 
365
434
  /**
366
- * the task is over: the button OK is shown. After an error it waits for a click;
367
- * without one it counts the seconds left, "OK (5s)", and closes the box at 0
435
+ * the task is over: the button OK is shown. After an error or a warning it waits for
436
+ * a click, their counts on the left of the bar ("2 errors / 1 warning"); without one it counts the
437
+ * seconds left, "OK (5s)", and closes the box at 0
368
438
  */
369
439
 
370
440
  done( ) {
371
441
  const ok = this.query<Button>( "#ok" );
372
442
  this.query( "#btnbar" ).show( true );
373
443
 
374
- if( this.#has_errors ) {
444
+ if( this._errors || this._warnings ) {
445
+ const tr = _tr.global.progression;
446
+ const counts = [];
447
+ if( this._errors ) {
448
+ counts.push( sprintf( this._errors>1 ? tr.errors : tr.error, this._errors ) );
449
+ }
450
+ if( this._warnings ) {
451
+ counts.push( sprintf( this._warnings>1 ? tr.warnings : tr.warning, this._warnings ) );
452
+ }
453
+
454
+ // on the left, the icon of the worst
455
+ const alerts = this.query<Label>( "#alerts" );
456
+ alerts.setText( counts.join( " / " ) );
457
+ alerts.setIcon( this._errors ? icons.danger : icons.warning );
458
+ alerts.setClass( "error", this._errors>0 );
459
+ alerts.setClass( "warning", this._errors==0 );
460
+
375
461
  ok.setText( _tr.global.ok );
376
462
  return;
377
463
  }
@@ -15,7 +15,7 @@
15
15
  **/
16
16
 
17
17
  import { ComponentProps } from '../../core/component';
18
- import { asap, class_ns, Rect, UnsafeHtml } from '../../core/core_tools';
18
+ import { asap, Rect, UnsafeHtml } from '../../core/core_tools';
19
19
 
20
20
  import { Popup } from '../popup/popup';
21
21
  import { HBox, VBox } from '../boxes/boxes';
@@ -61,18 +61,18 @@ interface NotificationProps extends ComponentProps {
61
61
  * ```
62
62
  */
63
63
 
64
- @class_ns( "x4" )
65
64
  export class Notification extends Popup {
65
+ static classPrefix = "x4";
66
+
66
67
  private static list = new Set<Notification>( );
67
68
 
68
69
  constructor( props: NotificationProps ) {
69
- super( { } );
70
+ super( props );
70
71
 
71
72
  let icon = props.iconId;
72
73
  if( !icon ) {
73
74
  if( props.loading ) {
74
75
  icon = icons.loading;
75
- this.addClass( "")
76
76
  }
77
77
  else if( props.mode=="danger" ) {
78
78
  icon = icons.danger;
@@ -84,10 +84,10 @@ export class Notification extends Popup {
84
84
 
85
85
  this.addClass( props.mode );
86
86
 
87
+ // the icon of loading turns (x4rotate, base.scss)
87
88
  const _icon = new Icon( { iconId: icon } );
88
89
  if( props.loading ) {
89
- _icon.addClass( "rotate" );
90
- //this.props.modal = true;
90
+ _icon.addClass( "x4rotate" );
91
91
  }
92
92
 
93
93
  this.setContent( new HBox( {
@@ -15,7 +15,7 @@
15
15
  **/
16
16
 
17
17
  import { Component, ComponentContent, ComponentProps } from '../../core/component';
18
- import { UnsafeHtml, Constructor, class_ns } from '../../core/core_tools';
18
+ import { UnsafeHtml, Constructor } from '../../core/core_tools';
19
19
 
20
20
  import { VBox } from '../boxes/boxes';
21
21
  import { Label } from '../label/label';
@@ -37,8 +37,8 @@ interface PanelProps extends ComponentProps {
37
37
  * ```
38
38
  */
39
39
 
40
- @class_ns( "x4" )
41
40
  export class Panel extends VBox<PanelProps> {
41
+ static classPrefix = "x4";
42
42
 
43
43
  private _title: Label;
44
44
  private _body: Component;
@@ -17,7 +17,7 @@
17
17
  import { Component, ComponentEvent, ComponentEvents, ComponentProps, componentFromDOM, makeUniqueComponentId } from "../../core/component"
18
18
 
19
19
  import { CSizer } from '../sizers/sizer';
20
- import { Rect, Point, class_ns, asap } from '../../core/core_tools';
20
+ import { Rect, Point, asap } from '../../core/core_tools';
21
21
  import { Box } from '../boxes/boxes'
22
22
 
23
23
  import "./popup.module.scss"
@@ -70,11 +70,12 @@ function dismissLast( e: UIEvent ) {
70
70
  * ```
71
71
  */
72
72
 
73
- @class_ns( "x4" )
74
73
  export class Popup<P extends PopupProps = PopupProps, E extends PopupEvents = PopupEvents> extends Box<P,E> {
74
+ static classPrefix = "x4";
75
75
 
76
76
  private _isshown = false;
77
77
  protected _ismodal = false;
78
+ private _prevFocus: HTMLElement; // modal: the element that had the focus when it opened
78
79
 
79
80
  constructor( props: P ) {
80
81
  super( props );
@@ -220,6 +221,11 @@ export class Popup<P extends PopupProps = PopupProps, E extends PopupEvents = Po
220
221
  return;
221
222
  }
222
223
 
224
+ // modal: the focus goes back to the element that had it when it opened, unless the
225
+ // user put it elsewhere (read before the popup leaves the page and its focus with it)
226
+ const active = document.activeElement;
227
+ const giveBack = this._ismodal && this._prevFocus && ( !active || active===document.body || this.dom.contains( active ) );
228
+
223
229
  this.__hide( );
224
230
  this.__remove( );
225
231
 
@@ -255,6 +261,11 @@ export class Popup<P extends PopupProps = PopupProps, E extends PopupEvents = Po
255
261
  }
256
262
  }
257
263
 
264
+ if( giveBack && this._prevFocus.isConnected ) {
265
+ this._prevFocus.focus( );
266
+ }
267
+ this._prevFocus = null;
268
+
258
269
  this._isshown = false;
259
270
  this.fire( "closed", {} );
260
271
  }
@@ -272,6 +283,10 @@ export class Popup<P extends PopupProps = PopupProps, E extends PopupEvents = Po
272
283
  this.__append( );
273
284
 
274
285
  if( this._ismodal ) {
286
+ // given back the focus when it closes; <body>: nothing had it
287
+ const active = document.activeElement as HTMLElement;
288
+ this._prevFocus = active && active!==document.body ? active : null;
289
+
275
290
  modal_stack.push( this );
276
291
  this._showModalMask( );
277
292
  }
@@ -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 "./progress.module.scss";
@@ -33,8 +32,8 @@ interface ProgressProps extends ComponentProps {
33
32
  * ```
34
33
  */
35
34
 
36
- @class_ns( "x4" )
37
35
  export class Progress extends Component<ProgressProps> {
36
+ static classPrefix = "x4";
38
37
 
39
38
  private _bar: Component;
40
39
 
@@ -117,9 +117,8 @@
117
117
  }
118
118
 
119
119
  // A row has no box of its own: its two cells go straight on the columns
120
- // of the grid, which is how all the titles get the same width. (A row is
121
- // a fieldset: it cannot be a subgrid.) What a row looked like, background
122
- // and bottom line, is carried by its cells.
120
+ // of the grid, which is how all the titles get the same width. What a row
121
+ // looked like, background and bottom line, is carried by its cells.
123
122
  .row {
124
123
  display: contents;
125
124
 
@@ -21,7 +21,7 @@ import { HBox, VBox } from "../boxes/boxes"
21
21
  import { Input } from "../input/input"
22
22
  import { ListboxID, ListItem } from "../listbox/listbox"
23
23
  import { Label, SimpleText } from "../label/label"
24
- import { asap, class_ns, isFunction, isNumber } from '../../core/core_tools';
24
+ import { asap, isFunction, isNumber } from '../../core/core_tools';
25
25
  import { Icon, ScrollView } from '../components'
26
26
  import { ColorInput } from '../colorinput/colorinput'
27
27
  import { EvColorChange } from '../colorpicker/colorpicker'
@@ -84,12 +84,15 @@ export interface PropertyProps extends ComponentProps {
84
84
  * ```
85
85
  */
86
86
 
87
- @class_ns( "x4" )
88
87
  export class PropertyGrid extends VBox {
88
+ static classPrefix = "x4";
89
89
 
90
90
  private root: Component;
91
91
  private groups: PropertyGroup[];
92
92
 
93
+ // the editor of each property, by its name: no id, a name is not unique in a page
94
+ private editors = new Map<string, Component>( );
95
+
93
96
  constructor(props: PropertyProps) {
94
97
  super(props);
95
98
 
@@ -118,6 +121,8 @@ export class PropertyGrid extends VBox {
118
121
 
119
122
  setItems( _grps: PropertyGroup[] ) {
120
123
 
124
+ this.editors.clear( );
125
+
121
126
  if( !_grps || _grps.length==0 ) {
122
127
  this.root.clearContent( );
123
128
  return;
@@ -218,7 +223,6 @@ export class PropertyGrid extends VBox {
218
223
  if (item.type === 'boolean') {
219
224
  editor = new Input({
220
225
  type: "checkbox",
221
- id: item.name,
222
226
  //name: item.name,
223
227
  checked: value as boolean,
224
228
  dom_events: {
@@ -232,7 +236,6 @@ export class PropertyGrid extends VBox {
232
236
  else if (item.type === 'options') {
233
237
  editor = new Select( {
234
238
  value: value as ListboxID,
235
- id: item.name,
236
239
  items: item.options,
237
240
  //name: item.name,
238
241
  change: ( e: EvChange ) => {
@@ -243,20 +246,23 @@ export class PropertyGrid extends VBox {
243
246
  else if (item.type === 'password') {
244
247
  editor = new Input({
245
248
  type: 'password',
246
- id: item.name,
247
- //name: item.name,
248
- value: String(value),
249
+ //name: item.name,
250
+ value: String(value),
249
251
  focus: ( e: EvFocus ) => {
250
252
  if( e.focus_out ) {
251
253
  item.callback?.( item.name, (editor as Input).getValue() );
252
254
  }
255
+ },
256
+ change: ( ) => {
257
+ if( item.live ) {
258
+ item.callback?.( item.name, (editor as Input).getValue() );
259
+ }
253
260
  }
254
261
  });
255
262
  }
256
263
  else if (item.type === 'number') {
257
264
  editor = new Input({
258
265
  type: 'number',
259
- id: item.name,
260
266
  //name: item.name,
261
267
  value: String(value),
262
268
  step: item.step,
@@ -276,7 +282,6 @@ export class PropertyGrid extends VBox {
276
282
  }
277
283
  else if (item.type === 'color') {
278
284
  editor = new ColorInput({
279
- id: item.name,
280
285
  color: value as string,
281
286
  format: "hex",
282
287
  picker: true,
@@ -289,14 +294,12 @@ export class PropertyGrid extends VBox {
289
294
  }
290
295
  else if (item.type === 'label') {
291
296
  editor = new Label({
292
- id: item.name,
293
297
  text: value as string,
294
298
  });
295
299
  }
296
300
  else if (item.type === 'button') {
297
301
  use_hdr = false;
298
302
  editor = new Button({
299
- id: item.name,
300
303
  cls: "button",
301
304
  label: value as string,
302
305
  click: ( ) => {
@@ -307,7 +310,6 @@ export class PropertyGrid extends VBox {
307
310
  else {
308
311
  editor = new Input({
309
312
  type: 'text',
310
- id: item.name,
311
313
  //name: item.name,
312
314
  value: value as string,
313
315
  focus: ( e: EvFocus ) => {
@@ -315,8 +317,17 @@ export class PropertyGrid extends VBox {
315
317
  item.callback?.( item.name, (editor as Input).getValue() );
316
318
  }
317
319
  },
320
+ change: ( ) => {
321
+ if( item.live ) {
322
+ item.callback?.( item.name, (editor as Input).getValue() );
323
+ }
324
+ }
318
325
  })
319
326
  }
327
+
328
+ if( item.name ) {
329
+ this.editors.set( item.name, editor );
330
+ }
320
331
 
321
332
  let cls = 'row';
322
333
  if( item.cls ) {
@@ -340,7 +351,7 @@ export class PropertyGrid extends VBox {
340
351
  cls,
341
352
  content: [
342
353
  use_hdr ? new Component({ cls: 'cell hdr', content: item.title ?? item.name, tooltip: item.desc }) : null,
343
- new HBox({ cls: 'cell', tag: "label", attrs: { "labelFor": item.name }, content }),
354
+ new HBox({ cls: 'cell', tag: "label", content }),
344
355
  ]
345
356
  });
346
357
  }
@@ -352,45 +363,28 @@ export class PropertyGrid extends VBox {
352
363
  setPropValue( name: string, value: any ) {
353
364
  const all = this.groups.flatMap( x => [ ...x.items ] ) ;
354
365
  const item = all.find( x => x.name==name );
355
- if( !item ) {
366
+ const editor = this.editors.get( name );
367
+ if( !item || !editor ) {
356
368
  return;
357
369
  }
358
370
 
359
371
  if (item.type === 'boolean') {
360
- const editor = this.root.query<Input>( '#'+item.name );
361
- if( editor ) {
362
- editor.setCheck( value as boolean );
363
- }
372
+ (editor as Input).setCheck( value as boolean );
364
373
  }
365
374
  else if (item.type === 'options') {
366
- const editor = this.root.query<Select>( '#'+item.name );
367
- if( editor ) {
368
- editor.setValue( value as string );
369
- }
375
+ (editor as Select).setValue( value as string );
370
376
  }
371
377
  else if (item.type === 'number') {
372
- const editor = this.root.query<Input>( '#'+item.name );
373
- if( editor ) {
374
- editor.setNumValue( value as number, -2 );
375
- }
378
+ (editor as Input).setNumValue( value as number, -2 );
376
379
  }
377
380
  else if (item.type === 'color') {
378
- const editor = this.root.query<ColorInput>( '#'+item.name );
379
- if( editor ) {
380
- editor.setColor( value as string );
381
- }
381
+ (editor as ColorInput).setColor( value as string );
382
382
  }
383
383
  else if (item.type === 'label') {
384
- const label = this.root.query<Label>( '#'+item.name );
385
- if( label ) {
386
- label.setText( value );
387
- }
384
+ (editor as Label).setText( value );
388
385
  }
389
386
  else {
390
- const editor = this.root.query<Input>( '#'+item.name );
391
- if( editor ) {
392
- editor.setValue( value as string );
393
- }
387
+ (editor as Input).setValue( value as string );
394
388
  }
395
389
  }
396
390
 
@@ -15,7 +15,6 @@
15
15
  **/
16
16
 
17
17
  import { Component, ComponentEvents, ComponentProps, EvChange, makeUniqueComponentId } from '../../core/component';
18
- import { class_ns } from '../../core/core_tools';
19
18
  import { EventCallback } from '../../core/core_events';
20
19
 
21
20
  import { Label } from '../label/label';
@@ -57,8 +56,8 @@ interface RadioProps extends ComponentProps {
57
56
  * ```
58
57
  */
59
58
 
60
- @class_ns( "x4" )
61
59
  export class Radio extends Component<RadioProps,RadioEvents> {
60
+ static classPrefix = "x4";
62
61
 
63
62
  private _check: HBox;
64
63
  private _input: Input;
@@ -18,7 +18,7 @@ import { Component, ComponentEvents, ComponentProps, EvChange } from '../../core
18
18
  import { EventCallback } from '../../core/core_events';
19
19
  import { HBox } from '../boxes/boxes';
20
20
  import { Icon } from '../icon/icon';
21
- import { class_ns, IComponentInterface, IFormElement } from '../../core/core_tools';
21
+ import { IComponentInterface, IFormElement } from '../../core/core_tools';
22
22
 
23
23
  import icons from "../assets/icons"
24
24
 
@@ -49,8 +49,8 @@ export interface RatingProps extends ComponentProps {
49
49
  * ```
50
50
  */
51
51
 
52
- @class_ns( "x4" )
53
52
  export class Rating extends HBox<RatingProps,RatingEventMap> {
53
+ static classPrefix = "x4";
54
54
 
55
55
  private m_els: Component[];
56
56
 
@@ -17,7 +17,7 @@
17
17
 
18
18
  import { EventCallback } from '../../core/core_events';
19
19
  import { Component, ComponentEvents, ComponentProps, EvChange, EvFocus } from '../../core/component';
20
- import { class_ns, IComponentInterface, IFormElement, isArray } from '../../core/core_tools';
20
+ import { IComponentInterface, IFormElement, isArray } from '../../core/core_tools';
21
21
 
22
22
  import { ListboxID, ListItem } from '../components';
23
23
 
@@ -57,8 +57,8 @@ interface SelectEvents extends ComponentEvents {
57
57
  * ```
58
58
  */
59
59
 
60
- @class_ns( "x4" )
61
60
  export class Select extends Component<SelectProps,SelectEvents> {
61
+ static classPrefix = "x4";
62
62
 
63
63
  private _items: ListItem[];
64
64
 
@@ -16,8 +16,9 @@
16
16
 
17
17
  :root {
18
18
  --settingsview-toc-width: 210px;
19
- --settingsview-section-background: var( --color-primary-a10 );
20
- --settingsview-section-title-background: var( --color-primary-a20 );
19
+ --settingsview-section-background: var( --background-primary );
20
+ --settingsview-section-title-background: transparent;
21
+ --settingsview-section-title-color: var( --accent-background );
21
22
  --settingsview-hover-background: var( --color-primary-a20 );
22
23
  --settingsview-marker: var( --accent-background );
23
24
  }
@@ -92,13 +93,13 @@
92
93
  }
93
94
  }
94
95
 
95
- // a section: a panel, its title on a band
96
+ // a section: no frame, its title in the accent colour on a line
96
97
  .section {
97
98
  flex-shrink: 0;
98
99
  width: 100%;
99
100
  max-width: 900px;
100
101
  padding: 0 8px 10px;
101
- border: 1px solid var( --border );
102
+ border: none;
102
103
  background: var( --settingsview-section-background );
103
104
  overflow: hidden;
104
105
  scroll-margin-top: 20px; // brought to the top: below the padding of the view
@@ -115,7 +116,7 @@
115
116
  background: var( --settingsview-section-title-background );
116
117
  font-size: 1.1rem;
117
118
  font-weight: 600;
118
- --label-color: var( --text-primary );
119
+ --label-color: var( --settingsview-section-title-color );
119
120
  }
120
121
 
121
122
  > .description {