x4js 2.2.40 → 2.2.42

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x4js",
3
- "version": "2.2.40",
3
+ "version": "2.2.42",
4
4
  "type": "module",
5
5
  "main": "src/x4.ts",
6
6
  "module": "src/x4.ts",
@@ -0,0 +1,10 @@
1
+
2
+ const x4icons = {
3
+ check: `<svg viewBox="0 0 10 7" fill="none" aria-hidden="true"><path d="M4 4.586L1.707 2.293A1 1 0 1 0 .293 3.707l3 3a.997.997 0 0 0 1.414 0l5-5A1 1 0 1 0 8.293.293L4 4.586z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>`,
4
+ empty: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path opacity=".4" d="M32 96l0 200.6 21.7-54.3C65.9 211.9 95.3 192 128 192l320 0 0-32c0-17.7-14.3-32-32-32l-117.5 0c-25.5 0-49.9-10.1-67.9-28.1L204.1 73.4c-6-6-14.1-9.4-22.6-9.4L64 64C46.3 64 32 78.3 32 96zM49.1 426.1c-2 4.9-1.4 10.5 1.6 14.9s7.9 7 13.2 7l320 0 80 0c6.5 0 12.4-4 14.9-10.1l64-160c2-4.9 1.4-10.5-1.6-14.9s-7.9-7-13.2-7l-400 0c-6.5 0-12.4 4-14.9 10.1l-64 160z"/><path d="M448 160l0 32 32 0 0-32c0-35.3-28.7-64-64-64L298.5 96c-17 0-33.3-6.7-45.3-18.7L226.7 50.7c-12-12-28.3-18.7-45.3-18.7L64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0 80 0c19.6 0 37.3-11.9 44.6-30.2l64-160c5.9-14.8 4.1-31.5-4.8-44.7S543.9 224 528 224l-400 0c-19.6 0-37.3 11.9-44.6 30.2L32 382.8 32 96c0-17.7 14.3-32 32-32l117.5 0c8.5 0 16.6 3.4 22.6 9.4l22.6-22.6L204.1 73.4l26.5 26.5c18 18 42.4 28.1 67.9 28.1L416 128c17.7 0 32 14.3 32 32zM384 448L64 448c-5.3 0-10.3-2.6-13.2-7s-3.6-10-1.6-14.9l64-160c2.4-6.1 8.3-10.1 14.9-10.1l400 0c5.3 0 10.3 2.6 13.2 7s3.6 10 1.6 14.9l-64 160C476.4 444 470.5 448 464 448l-80 0z"/></svg>`,
5
+ radio: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 5 5" aria-hidden="true"><circle class="fa-primary" cx="2.5" cy="2.5" r="2.5" ></circle><circle class="fa-secondary" cx="2.5" cy="2.5" r="1.25"></circle></svg>`,
6
+ chevron: `<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="M267.3 395.3c-6.2 6.2-16.4 6.2-22.6 0l-192-192c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0L256 361.4 436.7 180.7c6.2-6.2 16.4-6.2 22.6 0s6.2 16.4 0 22.6l-192 192z"/></svg>`,
7
+
8
+ };
9
+
10
+ export default x4icons;
@@ -3,12 +3,11 @@ import { EventCallback } from '../../core/core_events';
3
3
 
4
4
  import { Input } from '../input/input';
5
5
  import { Label } from '../label/label';
6
+ import { class_ns } from '../../core/core_tools';
6
7
 
7
- import { svgLoader } from '../icon/icon';
8
+ import icons from "../assets/icons";
8
9
 
9
10
  import "./checkbox.module.scss"
10
- import icon from "./check.svg";
11
- import { class_ns } from '../../core/core_tools';
12
11
 
13
12
  /**
14
13
  * Checkbox events
@@ -76,11 +75,7 @@ export class Checkbox extends Component<CheckboxProps,CheckBoxEvents> {
76
75
  } ),
77
76
  ])
78
77
 
79
-
80
- const svg = `<svg viewBox="0 0 10 7" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M4 4.586L1.707 2.293A1 1 0 1 0 .293 3.707l3 3a.997.997 0 0 0 1.414 0l5-5A1 1 0 1 0 8.293.293L4 4.586z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd">
81
- </path></svg>`
82
-
83
- this.query<Label>( '.inner' ).dom.insertAdjacentHTML( "beforeend", svg );
78
+ this.query<Label>( '.inner' ).dom.insertAdjacentHTML( "beforeend", icons.check );
84
79
 
85
80
  /*
86
81
  svgLoader.load( icon ).then( svg => {
@@ -37,6 +37,7 @@ export * from "./slider/slider"
37
37
  export * from "./spreadsheet/spreadsheet"
38
38
  export * from "./switch/switch"
39
39
  export * from "./tabs/tabs"
40
+ export * from "./tag/tag"
40
41
  export * from "./textarea/textarea"
41
42
  export * from "./textedit/textedit"
42
43
  export * from "./tickline/tickline"
@@ -27,12 +27,10 @@ import { Box } from '../boxes/boxes';
27
27
  import { CSizer } from '../sizers/sizer'
28
28
  import { Viewport } from '../viewport/viewport';
29
29
  import { Label, SimpleText } from '../label/label';
30
+ import icons from "../assets/icons"
30
31
 
31
32
  import { _tr } from '../../core/core_i18n';
32
33
 
33
- const check_icon = `<svg viewBox="0 0 10 7" fill="none" aria-hidden="true"><path d="M4 4.586L1.707 2.293A1 1 0 1 0 .293 3.707l3 3a.997.997 0 0 0 1.414 0l5-5A1 1 0 1 0 8.293.293L4 4.586z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>`;
34
- const empty_icon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path opacity=".4" d="M32 96l0 200.6 21.7-54.3C65.9 211.9 95.3 192 128 192l320 0 0-32c0-17.7-14.3-32-32-32l-117.5 0c-25.5 0-49.9-10.1-67.9-28.1L204.1 73.4c-6-6-14.1-9.4-22.6-9.4L64 64C46.3 64 32 78.3 32 96zM49.1 426.1c-2 4.9-1.4 10.5 1.6 14.9s7.9 7 13.2 7l320 0 80 0c6.5 0 12.4-4 14.9-10.1l64-160c2-4.9 1.4-10.5-1.6-14.9s-7.9-7-13.2-7l-400 0c-6.5 0-12.4 4-14.9 10.1l-64 160z"/><path d="M448 160l0 32 32 0 0-32c0-35.3-28.7-64-64-64L298.5 96c-17 0-33.3-6.7-45.3-18.7L226.7 50.7c-12-12-28.3-18.7-45.3-18.7L64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0 80 0c19.6 0 37.3-11.9 44.6-30.2l64-160c5.9-14.8 4.1-31.5-4.8-44.7S543.9 224 528 224l-400 0c-19.6 0-37.3 11.9-44.6 30.2L32 382.8 32 96c0-17.7 14.3-32 32-32l117.5 0c8.5 0 16.6 3.4 22.6 9.4l22.6-22.6L204.1 73.4l26.5 26.5c18 18 42.4 28.1 67.9 28.1L416 128c17.7 0 32 14.3 32 32zM384 448L64 448c-5.3 0-10.3-2.6-13.2-7s-3.6-10-1.6-14.9l64-160c2.4-6.1 8.3-10.1 14.9-10.1l400 0c5.3 0 10.3 2.6 13.2 7s3.6 10 1.6 14.9l-64 160C476.4 444 470.5 448 464 448l-80 0z"/></svg>`;
35
-
36
34
  import "./gridview.module.scss"
37
35
 
38
36
  export type CellRenderer = (rec: DataRecord) => Component;
@@ -677,7 +675,7 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
677
675
  switch (type) {
678
676
  case "checkbox": {
679
677
  if (data) {
680
- return new Icon({ cls: "cell-check" + cls, iconId: check_icon });
678
+ return new Icon({ cls: "cell-check" + cls, iconId: icons.check });
681
679
  }
682
680
 
683
681
  return undefined;
@@ -1141,7 +1139,7 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
1141
1139
 
1142
1140
  // empty ?
1143
1141
  if( rowc==0 ) {
1144
- this._body.setContent( new Label( { cls: "empty vertical", icon: empty_icon, text: this.props.emptyMsg ?? _tr.global.empty_list } ) );
1142
+ this._body.setContent( new Label( { cls: "empty vertical", icon: icons.empty, text: this.props.emptyMsg ?? _tr.global.empty_list } ) );
1145
1143
  return;
1146
1144
  }
1147
1145
 
@@ -22,15 +22,20 @@ import { ScrollView, Viewport } from '../viewport/viewport';
22
22
  import { Header } from '../header/header';
23
23
 
24
24
  import { HBox } from '../boxes/boxes';
25
- import { Label } from '../label/label';
25
+ import { Label, SimpleText } from '../label/label';
26
+
27
+ import { _tr } from '../../x4.js';
28
+ import icons from '../assets/icons'
26
29
 
27
30
  import "./listbox.module.scss"
28
31
 
32
+
29
33
  export type ListboxID = number | string;
30
34
 
31
35
  export interface ListItem {
32
36
  id: ListboxID;
33
37
  text: string | UnsafeHtml;
38
+ sub_cols?: (string | UnsafeHtml | Component)[]; // extra columns
34
39
 
35
40
  iconId?: string;
36
41
  data?: any;
@@ -62,6 +67,7 @@ export interface ListboxProps extends Omit<ComponentProps,'content'> {
62
67
  footer?: Component,
63
68
  checkable?: true,
64
69
  multisel?: true,
70
+ emptyMsg?: string,
65
71
 
66
72
  dblClick?: EventCallback<EvDblClick>;
67
73
  selectionChange?: EventCallback<EvSelectionChange>;
@@ -449,6 +455,7 @@ export class Listbox extends Component<ListboxProps,ListboxEvents> {
449
455
  }
450
456
  else {
451
457
  update_sel = oldSel.length>0;
458
+ this._view.setContent( new Label( { cls: "empty vertical", icon: icons.empty, text: this.props.emptyMsg ?? _tr.global.empty_list } ) );
452
459
  }
453
460
 
454
461
  if( update_sel ) {
@@ -477,9 +484,31 @@ export class Listbox extends Component<ListboxProps,ListboxEvents> {
477
484
  */
478
485
 
479
486
  defaultRenderer( item: ListItem ): Component {
487
+
488
+ const mk_col = ( _: any, index: number ) => {
489
+ const c = item.sub_cols[index];
490
+ if( c===undefined || c===null ) {
491
+ return null;
492
+ }
493
+
494
+ if( c instanceof Component ) {
495
+ return c;
496
+ }
497
+
498
+ return new SimpleText( { cls: `column ref-c${index+2}`, text: c } )
499
+ }
500
+
501
+ let cols: Component[] = null;
502
+ if( item.sub_cols ) {
503
+ cols = item.sub_cols.map( mk_col );
504
+ }
505
+
480
506
  return new HBox( {
481
507
  cls: item.cls,
482
- content: new Label( { icon: item.iconId, text: item.text })
508
+ content: [
509
+ new Label( { cls: `column ref-c1`, icon: item.iconId, text: item.text } ),
510
+ ...cols,
511
+ ],
483
512
  } )
484
513
  }
485
514
 
@@ -22,6 +22,8 @@ import { Label } from '../label/label';
22
22
  import { Input } from '../input/input'
23
23
  import { HBox } from '../boxes/boxes';
24
24
 
25
+ import icons from "../assets/icons"
26
+
25
27
  import "./radio.module.scss";
26
28
 
27
29
  /**
@@ -84,8 +86,7 @@ export class Radio extends Component<RadioProps,RadioEvents> {
84
86
  } ),
85
87
  ])
86
88
 
87
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 5 5" aria-hidden="true"><circle class="fa-primary" cx="2.5" cy="2.5" r="2.5" ></circle><circle class="fa-secondary" cx="2.5" cy="2.5" r="1.25"></circle></svg>`
88
- this._check.dom.insertAdjacentHTML( "beforeend", svg );
89
+ this._check.dom.insertAdjacentHTML( "beforeend", icons.radio );
89
90
 
90
91
  //svgLoader.load( icon ).then( svg => {
91
92
  // this._check.dom.insertAdjacentHTML( "beforeend", svg );
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @file tag.module.scss
3
+ * @author Etienne Cochard
4
+ * @copyright (c) 2026 R-libre ingenierie, all rights reserved.
5
+ **/
6
+
7
+ .x4tag {
8
+ color: black;
9
+ border-radius: 16px;
10
+ border: 1px solid var( --border );
11
+ padding: 2px 8px;
12
+ gap: 4px;
13
+ font-weight: normal;
14
+
15
+ .x4icon:empty {
16
+ display: none;
17
+ }
18
+
19
+ .x4icon {
20
+ }
21
+
22
+ .x4simpletext {
23
+ color: inherit;
24
+ }
25
+
26
+ &.alert {
27
+ color: var( --alert-color );
28
+ background-color: var( --alert-background );
29
+ }
30
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @file tag.ts
3
+ * @author Etienne Cochard
4
+ * @copyright (c) 2026 R-libre ingenierie, all rights reserved.
5
+ **/
6
+
7
+ import { ComponentProps } from '../../core/component';
8
+ import { class_ns, UnsafeHtml } from '../../core/core_tools';
9
+ import { HBox } from '../boxes/boxes';
10
+ import { Icon } from '../icon/icon';
11
+ import { SimpleText } from '../label/label';
12
+
13
+ import "./tag.module.scss";
14
+
15
+
16
+ export interface TagProps extends ComponentProps {
17
+ label?: string | UnsafeHtml;
18
+ icon?: string;
19
+ }
20
+
21
+
22
+ @class_ns( "x4" )
23
+ export class Tag extends HBox<TagProps> {
24
+
25
+ constructor( props: TagProps ) {
26
+ super( props );
27
+
28
+ this.setContent( [
29
+ new Icon( { iconId: props.icon } ),
30
+ new SimpleText( { text: props.label } ),
31
+ ])
32
+ }
33
+ }
@@ -63,7 +63,7 @@
63
63
  &>.label {
64
64
  gap: 4px;
65
65
 
66
- .x4label, .x4icon {
66
+ .x4label, .x4icon, .x4label #icon {
67
67
  color: inherit;
68
68
  }
69
69
 
@@ -19,13 +19,15 @@ import { EventCallback } from '../../core/core_events';
19
19
  import { Component, ComponentEvent, ComponentEvents, ComponentProps, EvClick, EvDblClick, EvSelectionChange, componentFromDOM } from '../../core/component';
20
20
 
21
21
  import { ScrollView, Viewport } from '../viewport/viewport';
22
- import { Label } from '../label/label';
22
+ import { Label, SimpleText } from '../label/label';
23
23
  import { ListboxID, ListItem } from '../listbox/listbox';
24
24
  import { Box, BoxProps, HBox, VBox } from '../boxes/boxes';
25
25
  import { Icon } from '../icon/icon';
26
26
 
27
+ import { _tr } from '../../x4.js';
28
+ import icons from '../assets/icons'
29
+
27
30
  import "./treeview.module.scss";
28
- import folder_icon from "./chevron-down-light.svg"
29
31
 
30
32
 
31
33
  //import folder_closed from "./folder-minus-light.svg"
@@ -53,6 +55,7 @@ export interface TreeItem extends ListItem {
53
55
  interface TreeviewProps extends Omit<ComponentProps,"content"> {
54
56
  items: TreeItem[];
55
57
  footer?: Component;
58
+ emptyMsg?: string;
56
59
  selectionChange?: EventCallback<EvSelectionChange>;
57
60
  dblClick?: EventCallback<EvDblClick>;
58
61
  click?: EventCallback<EvClick>;
@@ -83,7 +86,7 @@ class CTreeViewItem extends Box {
83
86
 
84
87
  if( item ) {
85
88
  this._label = new HBox( {cls:"label item", content: [
86
- this._icon = new Icon( { cls: "opener", iconId: item.children ? folder_icon : undefined } ),
89
+ this._icon = new Icon( { cls: 'arrow', iconId: item.children ? icons.chevron : undefined } ),
87
90
  new Label( { tag: "span", cls: "", text: item.text, icon: item.iconId } ),
88
91
  ]});
89
92
 
@@ -91,6 +94,23 @@ class CTreeViewItem extends Box {
91
94
  this._label.addClass( item.cls );
92
95
  }
93
96
 
97
+ if( item.sub_cols ) {
98
+ const mk_col = ( _: any, index: number ) => {
99
+ const c = item.sub_cols[index];
100
+ if( c===undefined || c===null ) {
101
+ return null;
102
+ }
103
+
104
+ if( c instanceof Component ) {
105
+ return c;
106
+ }
107
+
108
+ return new SimpleText( { cls: `column ref-c${index+2}`, text: c } )
109
+ }
110
+
111
+ this._label.appendContent( item.sub_cols.map( mk_col ) );
112
+ }
113
+
94
114
  this._label.setInternalData( "id", item.id );
95
115
 
96
116
  if( item.children ) {
@@ -133,7 +153,7 @@ class CTreeViewItem extends Box {
133
153
  }
134
154
 
135
155
  setItems( items: TreeItem[ ] ) {
136
- if( items ) {
156
+ if( items && items.length ) {
137
157
  const childs = items.map( itm => {
138
158
  return new CTreeViewItem( {}, itm );
139
159
  })
@@ -202,10 +222,13 @@ export class Treeview extends Component<TreeviewProps,TreeviewEvents> {
202
222
 
203
223
  const root = new CTreeViewItem( { cls: "root"}, null );
204
224
 
205
- if( items ) {
225
+ if( items && items.length ) {
206
226
  root.setItems( items );
207
227
  this._view.setContent( root );
208
228
  }
229
+ else {
230
+ this._view.setContent( new Label( { cls: "empty vertical", icon: icons.empty, text: this.props.emptyMsg ?? _tr.global.empty_list } ) );
231
+ }
209
232
  }
210
233
 
211
234
  private _on_click( ev: UIEvent ) {
@@ -416,8 +416,8 @@ export class Component<P extends ComponentProps = ComponentProps, E extends Comp
416
416
  * @param name - The suffix of the `data-` attribute (e.g., for `data-foo`, use `"foo"`).
417
417
  * @returns The string value of the `data-*` attribute, or `null` if not present.
418
418
  *
419
- * @cf setIntData/getIntData (number)
420
- * @cf setInternalData/getInternalData (typed data)
419
+ * @cf Component.setIntData, Component.getIntData (number)
420
+ * @cf Component.setInternalData, Component.getInternalData (typed data)
421
421
  */
422
422
 
423
423
  getData( name: string ) : string {
@@ -1 +0,0 @@
1
- <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="M267.3 395.3c-6.2 6.2-16.4 6.2-22.6 0l-192-192c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0L256 361.4 436.7 180.7c6.2-6.2 16.4-6.2 22.6 0s6.2 16.4 0 22.6l-192 192z"/></svg>